2013-10-17 4 views
59

私はコレクション上でいくつかの作業を行う非同期コンソールアプリケーションを作成しようとしています。私は非同期/待機を使用する別のバージョンの並列forループを使用する1つのバージョンを持っています。私は非同期/待機バージョンが並列バージョンと同様に動作することを期待しましたが、同期的に実行します。私は間違って何をしていますか?awaitをループで使用する方法

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

私は他人についてはわかりませんが、並列for/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

私はあなたの答えを受け入れたいと思っていますが、Tims Sはより良い答えを持っています。 – Satish

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