我正在尝试创建一个异步控制台应用程序,对集合进行一些操作。我有一个版本使用并行for循环,使用异步/等待。我预计异步/等待版本的工作类似于并行版本,但它同步执行。是什么原因呢?
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 var worker = new Worker(); 6 worker.ParallelInit(); 7 var t = worker.Init(); 8 t.Wait(); 9 Console.ReadKey(); 10 } 11 } 12 13 public class Worker 14 { 15 public async Task<bool> Init() 16 { 17 var series = Enumerable.Range(1, 5).ToList(); 18 foreach (var i in series) 19 { 20 Console.WriteLine("Starting Process {0}", i); 21 var result = await DoWorkAsync(i); 22 if (result) 23 { 24 Console.WriteLine("Ending Process {0}", i); 25 } 26 } 27 28 return true; 29 } 30 31 public async Task<bool> DoWorkAsync(int i) 32 { 33 Console.WriteLine("working..{0}", i); 34 await Task.Delay(1000); 35 return true; 36 } 37 38 public bool ParallelInit() 39 { 40 var series = Enumerable.Range(1, 5).ToList(); 41 Parallel.ForEach(series, i => 42 { 43 Console.WriteLine("Starting Process {0}", i); 44 DoWorkAsync(i); 45 Console.WriteLine("Ending Process {0}", i); 46 }); 47 return true; 48 } 49 }
使用await
关键字的方式告诉C#,你希望每次通过循环时都等待,而循环并不平行。你可以像这样重写你的方法来做你想做的事情,通过存储Task
s 的列表然后将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); }
await
在开始下一次迭代之前,你的代码会等待每个操作(使用)完成。 因此,你没有任何并行性。
如果你想并行运行一个现有的异步操作,你不需要await
; 你只需要得到一个Task
s 的集合并且调用Task.WhenAll()
返回一个等待它们的任务:
return Task.WhenAll(list.Select(DoWorkAsync));
代码: