2013-07-22 22 views
4

我在使用一次性對象時試圖並行處理多個任務(或者運行時感覺適合)時遇到了問題。在以下代碼片段中,每個Processor對象在完成所需工作之前立即進行處理。當使用一次性對象時使用等待異步

async public Task ProcessData(IEnumerable<int> data) 
{ 
    var tasks = new List<Task>(); 

    foreach (var d in data) 
    { 
     using (var processor = new Processor(d)) 
     { 
      processor.Completed += (sender, e) => { // Do something else }; 
      tasks.Add(processor.ProcessAsync()); 
     } 
    } 

    await Task.WhenAll(tasks); 
} 

重新編寫的代碼,如下的結果在每個處理器中執行其處理,然後被設置,但它不是以運行不依賴於彼此的多個任務的最有效方式。

async public Task ProcessData(IEnumerable<int> data) 
{ 
    foreach (var d in data) 
    { 
     using (var processor = new Processor(d)) 
     { 
      processor.Completed += (sender, e) => { // Do something else }; 
      await processor.ProcessAsync(); 
     } 
    } 
} 

有人可以解釋爲什麼第一個例子是「提前」處置,並舉例說明這種情況的最佳代碼模式。

+0

爲什麼你需要將處理器對象放置在該範圍內?無論如何,處理器對象會在循環中的每個回合都離開使用範圍時被釋放。 – Christian

+0

我想我希望編譯器能夠在封面下運行一些魔法,並自動將處置設置爲一系列延續。 –

+0

編譯器*將*處理器設置爲一系列延續,但只有在'using'塊中有'await'時。第一個例子的根本問題是,編譯器絕對無法知道每個「處理器」的處理需要與任何特定任務的生命週期相關聯。 –

回答

7

它可以幫助想await作爲暫停當前方法,即使它不會阻止線程

在你的第一個例子,當您正在執行的foreach循環,每次創建Processor,開始操作(保存操作的Task在列表中),然後處置Processor。當您的foreach循環完成後,您(異步)等待所有操作完成。

在你的第二個例子,當您正在執行的foreach循環,每次創建Processor,開始操作,(異步)等待它完成,然後再處置Processor

爲了解決這個問題,你應該寫一個輔助方法,因爲這樣的:

private static async Task ProcessData(int data) 
{ 
    using (var processor = new Processor(d)) 
    { 
    processor.Completed += (sender, e) => { /* Do something else */ }; 
    await processor.ProcessAsync(); 
    } 
} 

您的輔助方法,定義了一個更高級別的操作,這將需要在適當的時候處置自己的Processor資源的照顧。然後你就可以同時啓動所有工作這樣:

public async Task ProcessData(IEnumerable<int> data) 
{ 
    ... 
    await Task.WhenAll(data.Select(d => ProcessData(d))); 
    ... 
} 
+0

幫助器方法是這個優雅解決方案的關鍵。它圍繞簡單的ProcessAsync封裝了一個更復雜的異步工作流程。 – usr

+0

是的,這是*多*清潔,我建議的方法:) –

2

雖然斯蒂芬·克利裏的回答是優雅,可能是一個更好的選擇在這種情況下,我認爲這將會是值得提請注意的東西,你得到的部分Reactive Extensions(Rx)可用於類似的場景。它提供了一系列有關IDisposable幫手,這將使你要做到這一點:

public async Task ProcessData(IEnumerable<int> data) 
{ 
    var tasks = new List<Task>(); 

    using (var disp = new CompositeDisposable()) 
    { 
     foreach (var d in data) 
     { 
      var processor = new Processor(d); 
      disp.Add(processor); 
      processor.Completed += (sender, e) => 
       { 
        // Do something else 
       }; 
      tasks.Add(processor.ProcessAsync()); 
     } 
     await Task.WhenAll(tasks); 
    } 
} 

要獲取CompositeDisposable,你需要一個的NuGet參考Rx-Main

我不認爲我會在這種情況下使用它,但我想我會盡量避免到一個需要看起來像這樣的代碼的地方。同時有Task代表您正在完成Processor的工作,也是一個事件來指示......某事?..它是完整的..?那麼Task可以爲你做,所以爲什麼都有呢?事件是相對混亂的 - 我總是發現Rx IObservable<T>或直接使用Task是比依賴事件更好的解決方案。

+0

謝謝伊恩。是的,當我和馬修坐下時,我們討論了從事件模型中移開。這只是一個尚未完全異步的代碼示例。 –