2013-10-17 27 views
59

我試圖創建一個異步的控制檯應用程序,並在集合的一些工作。我有一個版本使用並行for循環另一個版本,使用異步/等待。我預計異步/等待版本的工作類似於並行版本,但它同步執行。我究竟做錯了什麼?如何使用在等待一個循環

class Program 
{ 
    static void Main(string[] args) 
    { 
     var worker = new Worker(); 
     worker.ParallelInit(); 
     var t = worker.Init(); 
     t.Wait(); 
     Console.ReadKey(); 
    } 
} 

public class Worker 
{ 
    public async Task<bool> Init() 
    { 
     var series = Enumerable.Range(1, 5).ToList(); 
     foreach (var i in series) 
     { 
      Console.WriteLine("Starting Process {0}", i); 
      var result = await DoWorkAsync(i); 
      if (result) 
      { 
       Console.WriteLine("Ending Process {0}", i); 
      } 
     } 

     return true; 
    } 

    public async Task<bool> DoWorkAsync(int i) 
    { 
     Console.WriteLine("working..{0}", i); 
     await Task.Delay(1000); 
     return true; 
    } 

    public bool ParallelInit() 
    { 
     var series = Enumerable.Range(1, 5).ToList(); 
     Parallel.ForEach(series, i => 
     { 
      Console.WriteLine("Starting Process {0}", i); 
      DoWorkAsync(i); 
      Console.WriteLine("Ending Process {0}", i); 
     }); 
     return true; 
    } 
} 

回答

78

您使用的await關鍵字的方式告訴你要等待C#每次你通過循環,這是不平行的。你可以重寫你這樣的方法去做你想做的,通過存儲的Task秒的列表,然後await荷蘭國際集團所有這些與Task.WhenAll

public async Task<bool> Init() 
{ 
    var series = Enumerable.Range(1, 5).ToList(); 
    var tasks = new List<Task<Tuple<int, bool>>>(); 
    foreach (var i in series) 
    { 
     Console.WriteLine("Starting Process {0}", i); 
     tasks.Add(DoWorkAsync(i)); 
    } 
    foreach (var task in await Task.WhenAll(tasks)) 
    { 
     if (task.Item2) 
     { 
      Console.WriteLine("Ending Process {0}", task.Item1); 
     } 
    } 
    return true; 
} 

public async Task<Tuple<int, bool>> DoWorkAsync(int i) 
{ 
    Console.WriteLine("working..{0}", i); 
    await Task.Delay(1000); 
    return Tuple.Create(i, true); 
} 
+2

我不知道其他人,但並行/ foreach似乎更直接的並行循環。 – Brettski

+2

需要注意的是,當任務實際結束時,看到「Ending Process」通知**不是**。所有這些通知都在最後一個任務完成後依次排出。當你看到「結束進程1」時,進程1可能已經結束了很長時間。除了在那裏選擇單詞外,還有+1。 –

+0

@Brettski我可能是錯的,但是一個並行循環捕捉任何異步結果。通過返回任務你立刻回去任務對象中,你可以管理是怎麼回事內如取消它,或看到異常的工作。現在使用Async/Await,您可以以更友好的方式使用Task對象 - 這就是您不必執行Task.Result。 –

27

你的代碼等待每個操作(使用await)開始下一次迭代之前完成。
因此,你沒有得到任何並行。

如果你想並行運行現有的異步操作,你不需要await;你只需要得到Task秒的採集和調用Task.WhenAll()以返回等待所有這些任務:

return Task.WhenAll(list.Select(DoWorkAsync)); 
+0

所以你不能在任何循環中使用任何異步方法? – Satish

+3

@Satish:你可以。然而,「await」與你想要的完全相反 - 它等待「Task」完成。 – SLaks

+0

我想接受你的答案,但蒂姆斯有更好的答案。 – Satish

7
public async Task<bool> Init() 
{ 
    var series = Enumerable.Range(1, 5); 
    Task.WhenAll(series.Select(i => DoWorkAsync(i))); 
    return true; 
}