2015-09-28 106 views
0

我無法弄清楚這一點......我遵循了我在各處找到的幾種不同的說明,但我無法找到如何在執行幾項任務時不凍結UI線程。如何不凍結UI線程?

我在窗口加載時運行它。 在MainWindow.xaml窗口的根,我有:

<i:Interaction.Triggers> 
    <i:EventTrigger EventName="ContentRendered"> 
     <i:InvokeCommandAction Command="{Binding WindowLoaded}" /> 
    </i:EventTrigger> 
</i:Interaction.Triggers> 

觸發如下:

private void WindowLoadedEx(object p) 
{ 
    DBMan(); 
    LoadValues(); 
    //Boot = false; 
    //MainMenuVisibility = true; 
} 

DBMAN()是微不足道的,問題不在這裏 - 這是在LoadValues() 。我砍了很多,嘗試了各種不同的東西,沒有任何工作。

private void LoadValues() 
{ 
    //ThreadStart AddHeroesRef = new ThreadStart(HeroesDBAddHeroes); 
    //Thread AddHeroesThread = new Thread(AddHeroesRef); 
    //AddHeroesThread.Start(); 
    ////HeroesDBAddHeroes(); 
    //ThreadStart AddCommentsRef = new ThreadStart(HeroesDBAddComments); 
    //Thread AddCommentsThread = new Thread(AddCommentsRef); 
    //AddCommentsThread.Start(); 
    ////HeroesDBAddComments(); 
    ThreadStart LoadValuesRef = new ThreadStart(LoadValuesThreaded); 
    Thread LoadValuesThread = new Thread(LoadValuesRef); 
    LoadValuesThread.Start(); 
    //HeroesDBAssignComments(); 
    //SetDBIDs(); 
} 

private async void LoadValuesThreaded() 
{ 
    ThreadStart AddHeroesRef = new ThreadStart(HeroesDBAddHeroes); 
    Thread AddHeroesThread = new Thread(AddHeroesRef); 
    AddHeroesThread.Start(); 
    //HeroesDBAddHeroes(); 
    ThreadStart AddCommentsRef = new ThreadStart(HeroesDBAddComments); 
    Thread AddCommentsThread = new Thread(AddCommentsRef); 
    AddCommentsThread.Start(); 
    //HeroesDBAddComments(); 

    while (AddHeroesThread.IsAlive || AddCommentsThread.IsAlive) 
    { 
     Thread.Sleep(1); 
    } 
    HeroesDBAssignComments(); 
    SetDBIDs(); 

    Boot = false; 
    MainMenuVisibility = true; 
} 

(正如你所看到的,我試圖改變不同的東西,並且不能得到任何工作)。基本上,HeroesDBAddHeroes()和HeroesDBAddComments()都需要一起運行,它們之間沒有任何衝突。我將來還會有其他方法在這裏運行,同時也在單獨的線程中運行。

但是,在繼續並允許HeroesDBAssignComments()運行之前,我需要等待這些完成。如果上面的所有內容都不完整,它不會工作。然後,一旦HeroesDBAssignComments完成,我需要更改布爾值Boot和MainMenuVisibility。

+0

你有沒有試過調用'Dispatcher.Invoke(...)'方法? –

+2

你不應該使用Thread.Sleep()來等待。改用[WaitHandle] [1]。 [1]:http://stackoverflow.com/questions/2538065/what-is-the-basic-concept-behind-waithandle – Hristo

+0

哪裏做呢? – pingu2k4

回答

0

你不應該使用Thread.Sleep()來等待。改爲使用WaitHandle

使用WaitHandle的基本方法是在您希望等待的地方調用WaitOne(),然後在另一個線程上執行工作,然後在waithandle上調用Set()。

我通常使用EventWaitHandle

+0

這仍然會阻塞UI線程,凍結應用程序。 – Servy

1

既然你沒有指定一個.NET版本,我會只是建議處理這個的4.0方式:

void BlockingMethod() { 
    Task.Factory.StartNew(() => { 
     // blocking work here, db loading for example 
    }) 
    .ContinueWith(result => { 
     // update control here with the result of result.Result 
     }, 
     TaskScheduler.FromCurrentSynchronizationContext() 
    }); 
} 

您可能需要修復了語法一點。

這是幹什麼的?

基本上,當調用BlockingMethod時,它將啓動一個異步任務。這個方法在你的情況下會被UI調用,這就是你的UI凍結的原因。

因爲我們是異步的,所以我們在這裏執行的代碼將會繼續運行,當方法已經離開時,因爲它的工作正在被框架處理。

重載加載完成後,將執行「ContinueWith」中的代碼。我們傳遞TaskScheduler.FromCurrentSynchronizationContext(),因爲我們需要確保這個代碼只在方法最初從(ui同步上下文)調用的上下文中執行,否則你會得到一個異常,因爲wpf不允許這個。

1

使用基於任務的異步模式(TAP)使這樣的問題非常簡單。

您的頂級方法WindowLoadedEx可以聲明爲async void,因爲它基本上是一個榮耀的事件處理程序。然後,你可以使用async/await與捕獲的WPF同步上下文發佈在正確的線程用戶界面的變化,即:

private async void WindowLoadedEx(object p) 
{ 
    DBMan(); 
    await LoadValuesAsync(); 
    Boot = false; 
    MainMenuVisibility = true; 
} 

private async Task LoadValuesAsync() 
{ 
    var addHeroesTask = HeroesDBAddHeroesAsync(); 
    var addCommentsTask = HeroesDBAddCommentsAsync(); 

    // We cannot assign comments or IDs until these have been added 
    await Task.WhenAll(addHeroesTask, addCommentsTask).ConfigureAwait(false); 

    // TODO Should these both also be asynchronous? 
    HeroesDBAssignComments(); 
    SetDBIDs(); 
} 

通知我用HeroesDBAddHeroesAsyncHeroesDBAddCommentsAsync,因爲我相信這是真正的異步數據庫操作。

閱讀關於使用async/await here