2012-10-03 34 views
5

我們有一個webservice函數,它在後臺運行任務列表。所有任務完成後,它將返回。我再訪功能C#5,我已經成功地得到了下面的代碼片段工作:做使用async/await重寫多線程等待

var result = new List<int>(); 
var tasks = new List<Task<int>>(); 

foreach (var row in rowlist) 
    tasks.Add(Task.Factory.StartNew(() => DoWork(row))); 
foreach (var task in tasks) 
    result.Add(task.Result); 

task.Result等待單個任務。我已經測試了這個方法,爲DoWork方法添加了3秒的延遲,並驗證了它在不到5秒的時間內完成了10個任務的列表。

現在我試着用新的async/await語法來重寫函數。但我似乎無法讓它工作。如果編譯,它會同步運行,通過添加等待來驗證。

有關如何使用async/await重寫上述代碼片段的任何想法?

回答

9

我會寫爲:

var tasks = rowlist.Select(row => Task.Run(() => DoWork(row))); 

var results = (await Task.WhenAll(tasks)).ToList(); 

基本上,你還是創建任務,您以前(不使用await)的方式,只要你想他們都立即開始。然後,您可以等待所有人異步完成(通過await Task.WhenAll),然後立即提取結果(因爲您知道他們都完成了)。

+0

這運行異步。但它會導致'System.AggregateException',內部異常'{「查詢結果不能被枚舉超過一次。」}「任何想法? – Andomar

+0

也許更重要的是:如果你用'await'省略這行,爲什麼它會同步運行? – Andomar

+1

@Andomar在創建任務時,在選擇後調用'ToList'。這應該可以解決這個錯誤。目前,當枚舉幾次「任務」時,它會嘗試兩次創建新任務,這很糟糕。 – Servy

2

基於裏德•科普塞,Servy和斯蒂芬·克利裏的回答和評論我已經進步到這個解決方案:

var results = rowlist 
    .Select(row => Task.Run(() => DoWork(row))) 
    .ToList() 
    .Select(t => t.Result); 

第二Select將等待完成的任務。我在兩者之間添加了一個ToList()以防止流式傳輸,但似乎沒有這樣做。

+1

這與您開始使用的代碼基本相同。你需要在那裏等待... – Servy

+0

@Servy:現在我很困惑:是不是你評論過上面那個'task.Result'也可以阻止?代碼的工作方式如下:4秒內完成10個任務,每個任務都有3秒的休眠時間。 – Andomar

+2

使用異步/等待的關鍵在於整個方法(即代碼所在的位置)不會被阻塞。您可以從GUI應用程序中調用例如事件的方法,並且不會阻塞UI線程(同時仍在UI線程中運行所有需要的代碼)。使用'await'方法實際上不會返回結果,它會返回一個任務,這些任務會告訴您何時計算這些結果(以及可能的結果是什麼)。 – Servy