2014-10-19 157 views
1

我正在運行後臺線程從數據庫中獲取數據,以便當前線程(UI)不凍結。我正在使用TPL來實現這一點。如何使父線程等待子線程完成 - C#

List<string> _myList1, _myList2; 
private void LoadCollections(){ 
    Task db_task = new Task(() => CallDB()); 
    var continuations = db_task.ContinueWith((ant) => 
    { 
     _myList1 = GetDataFromMetaData1(); 
     _myList2 = GetDataFromMetaData2(); 
    }); 

    db_task.Start(); 
    LoadTemplates(); //execute only after 'db_task' completes execution 
} 

在上面的代碼

CallDB() - 獲取從在元數據數據庫中,並將其存儲的數據。在這種情況下,請考慮它是一個字符串列表。 (說List<string> MetaStrings

GetDataFromMetaData1()GetDataFromMetaData2() - 獲取2個單獨的名單,這是MetaStrings子目錄。

現在我只希望在加載_myList1_myList2之後,即db_task已經完成執行之後,執行函數LoadTemplates。目前,我正在使用這個

while(_mylist1==null && _mylist2==null) 
    Thread.Sleep(50); 

LoadTemplates(); 

但是,當UI線程進入睡眠狀態時,這顯然會凍結UI。那麼任何人都可以提出一種有效的方法來處理這種情況?

PS:LoadTemplates()初始化一些ObservableCollection變量。如果在子線程中調用LoadTemplates(),則db_task則引發以下錯誤This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread。因此產生了線程等待的情況。

+0

不能使用異步&等待? – terrybozzio 2014-10-19 23:28:32

+0

實際上'LoadCollections'函數是一個信使事件。換句話說,當用戶點擊UI上的一個按鈕時,ViewModel註冊來調用'LoadCollections'。所以在這種情況下,我有點失落了如何使用async/await。 – SohamC 2014-10-19 23:32:10

+0

不能使用Task.WaitAll(任務); – user1274820 2014-10-20 00:04:18

回答

5

你在問錯誤的問題。在輔助線程上運行進程時,您絕不希望主線程等待以完成操作。這只是一個浪費&hellip;你也可能只是在主線程上運行這個操作並完成它。

所以相反,問題是:我如何讓主(父)線程處理輔助(子)線程的完成?

根據您得到的錯誤消息,看起來您正在使用WPF編寫此代碼。這意味着詢問的問題的一個答案是,您可以從響應完成輔助線程的事件處理程序中調用Dispatcher.Invoke。

正如Terry所建議的那樣,您可以使用異步/等待功能。例如,這威力工作(很難不完整的代碼示例說):

List<string> _myList1, _myList2; 
private async void LoadCollections(){ 
    Task db_task = new Task(() => CallDB()); 

    var continuations = db_task.ContinueWith((ant) => 
    { 
     _myList1 = GetDataFromMetaData1(); 
     _myList2 = GetDataFromMetaData2(); 
    }); 

    db_task.Start(); 

    await continuations; 

    LoadTemplates(); //execute only after 'db_task' completes execution 
} 

換句話說,繼續前進,因爲你現在做安排您的工作,但使用異步和等待設置因此LoadTemplates()只有在繼續完成後纔會被調用(這反過來在初始數據庫任務完成之前不會被執行),特別是在主Dispatcher線程中執行LoadTemplates() LoadCollections首先被調用。

請注意,理想情況下,LoadCollections的返回類型實際上是Task。編譯器更喜歡這一點,它可以讓你更好地處理可能出現的異常。但是void應該沒問題,並且在處理事件處理函數方法時並不少見。

編輯:我也意味着提,涉及更徹底改變代碼的替代但恕我直言,是更符合異步/ AWAIT模式保持是,要麼簡單地等待原db_task然後執行「延續」語句內聯(即不在任務中),或者(如果這些語句本身是長時間運行的並且應該在後臺執行)仍然等待原始任務,然後還等待繼續作爲他們自己的任務,而不是顯式地作爲數據庫任務。

例如(假設延續操作簡單,並且可以在主線程上運行):

List<string> _myList1, _myList2; 
private async void LoadCollections(){ 
    await Task.Run(() => CallDB()); 

    _myList1 = GetDataFromMetaData1(); 
    _myList2 = GetDataFromMetaData2(); 

    LoadTemplates(); //execute only after 'db_task' completes execution 
} 
+0

我正在嘗試使用'Dispatcher.Invoke'來解決你的問題,'Dispatcher.CurrentDispatcher.Invoke(LoadTemplates,DispatcherPriority.ContextIdle);'。但這似乎並不奏效。你是否知道如何使Dispatcher.Invoke響應完成的輔助線程。 – SohamC 2014-10-20 07:25:58

+0

首先,定義「似乎不起作用」。這可能是很多不同的事情。其次,爲什麼使用ContextIdle作爲優先級?我的意思是,這應該可以工作,但如何開始與正常,然後從那裏去。第三,雖然使用分派器應該工作,異步/等待是恕我直言,更簡單,併產生更清晰的代碼。你是否嘗試過這種方式呢? – 2014-10-20 08:04:49

+0

解決方案運行。謝謝!!這並不重要,但我收到一條警告消息:'因爲這個調用沒有被等待,所以在調用完成之前繼續執行當前的方法。考慮將「await」操作符應用於調用的結果。「你知道它意味着什麼! – SohamC 2014-10-22 07:04:37

0

如果我理解你正確地質疑CallDB()必須先運行,那麼GetDataFromMetaData功能可以運行,然後最後LoadTemplates。我會使用ansy等待如下:

private async void LoadCollections() 
    { 

     await Task.Run(() => CallDB()); 

     Task[] tasks = new Task[1] 
     { 
      _myList1 = GetDataFromMetaData1(), 
      _myList2 = GetDataFromMetaData2() 
     }; 

     await Task.WhenAll(tasks); 

     LoadTemplates(); //execute only after 'db_task' completes execution 

    }