2017-06-07 77 views
-1

我有一段代碼測試GUI和線程的行爲。我想保持進度動畫運行(與IsIndeterminate="True")作爲我查詢數據庫,並添加了大量的行(10K +)到DataGrid中的。即使我在Dispatcher.BeginInvoke包數據庫和GUI代碼,如在DataGrid被填充進度動畫將這個混蛋。我希望ProgressBar動畫會凍結(如果在GUI線程上)或平穩運行(如果在一個單獨的線程上),但我不明白爲什麼動畫正在快速運行。不定進度的動畫加加速度在Dispatcher.BeginInvoke

請不要建議BackgroundWorker,因爲我想了解這個問題的問題,以及爲什麼BeginInvoke不分離線程。我只是簡單地循環訪問SqlDataReader並逐個添加到DataGrid中,而不是將數據綁定到源或數據表。

// XAML 
<Button Click="Button_Click"></Button> 
<ProgressBar IsIndeterminate="True"></ProgressBar> 
<DataGrid ... ></DataGrid> 

// C# 
private void Button_Click(object sendoer, RoutedEventArgs e) 
{ 
    this.Dispatcher.BeginInvoke(DispatcherPriority.Normal,(ThreadStart)delegate() 
    { 
     // Query database and update GUI (e.g. DataGrid) 
    }); 
} 
+0

進度條動畫在UI線程上運行。 WinRT能夠在不同的線程上運行動畫,但不幸的是WPF仍然無法使用該動畫。至於知道它爲什麼會亂動,你如何將數據綁定到DataGrid? –

+0

我只是簡單地循環訪問SqlDataReader,然後逐個添加到DataGrid中,而不是將數據綁定到源或數據表。 – KMC

+1

在你的代碼示例中,你所有的UI線程工作(使用BeginInvoke),那爲什麼它會流暢呢? – Evk

回答

3

Dispatcher總是執行它與(在你的情況下,UI線程)相關的線程上的代碼,無論你是否使用InvokeInvokeAsync(這是BeginInvoke方便的速記)。因此,所有關於從數據庫中裝載的數據和更新DataGrid的工作是在UI線程中完成的,因此動畫並不順利。

InvokeInvokeAsync之間的差別在於前者執行同步,後者執行異步。它的意思是,在第一種情況下調用線程將被掛起,直到代表已完成執行(即,它會被同步),而在第二種情況下,線程將繼續執行,而無需等待委託來完成。讓我試着用例子指出這種差異。

例一的方法是從UI線程調用(如你的情況)

假設我們只有一個線程(UI線程)。調用Invoke將不會有任何明顯的效果,因爲委託將被立即執行,然後纔會繼續執行。所以這樣的:

Dispatcher.Invoke(() => DoSomeStuff()); 
DoSomeOtherStuff(); 

都會有,因爲這同樣的效果:

DoSomeStuff(); 
DoSomeOtherStuff(); 

調用BeginInvoke但是也會有所影響,使得代表將被安排具有較高優先級只執行後所有預定的任務(或者已經按照相同的優先級計劃)被執行。因此,在這種情況下:

Dispatcher.InvokeAsync(() => DoSomeStuff()); 
DoSomeOtherStuff(); 

DoSomeOtherStuff()將首先執行,並DoSomeStuff()秒。這通常用於例如在需要一些代碼只有在事件被完全處理將被執行的事件處理程序(例如see this question)。

例二。這些方法是從不同的線程調用的

假設我們有兩個線程 - UI線程和工作線程。如果我們調用Invoke從工作線程:

Dispatcher.Invoke(() => DoSomeStuff()); 
DoSomeOtherStuff(); 

第一DoSomeStuff()將在UI線程中執行,然後DoSomeOtherStuff()將在工作線程執行。在InvokeAsync情況:

Dispatcher.InvokeAsync(() => DoSomeStuff()); 
DoSomeOtherStuff(); 

我們只知道DoSomeStuff()將在UI線程上執行,並DoSomeOtherStuff()將在工作線程執行,但在他們將要執行的順序是不確定*。

通常,Invoke用於代理產生某些結果並且需要它繼續在工作線程上執行(例如,當您需要獲取依賴項屬性值時)。 InvokeAsync另一方面通常用於代表不會產生任何結果(或結果被忽略)的情況下,例如在您的情況下 - 更新DataGrid不會產生值得等待的結果,因此您可以立即繼續加載下一批數據的。

我希望能夠爲您解決這個問題,您可以看到爲什麼「生澀UI」的解決方案是將繁重的工作委託給另一個線程,並且只使用調度程序與UI進行交互。這是建議使用BackgroundWorkerTask來自。 *其實他們可能會同時執行。我的意思是,如果例如兩種方法僅向控制檯打印一些文本,則控制檯中的消息順序是不確定的。

+0

'@ Grx70'明確解釋,謝謝。由於應用程序是在UIThread上啓動的,因此與UIThread有關的所有內容(例如Main()中的新Thread())都屬於它。因此'Dispatcher'不創建線程,但其功能是簡單地在同一個線程上同步或異步優先處理不同的作品,或允許UI擁有的線程訪問UIThread。那麼「BackgroundWorker」也是這種情況嗎?因爲如果我在BGW的DoWork上調用Dispatcher.BeginInvoke來更新UIThread,我仍然會遇到線程異常。你能幫助澄清這種理解嗎? – KMC

+0

是的,調度員也應該是與'BackgroundWorker'一起使用的安全選擇。如果您遇到問題,最好問問新問題並提供[MCVE](https://stackoverflow.com/help/mcve)。 – Grx70

+0

[鏈接](https://stackoverflow.com/questions/44409234/backgroundworker-to-read-database-and-update-gui) – KMC