2015-12-19 31 views
12

基本的には、私はたくさんのタスク(10)を持っていて、それらをすべて同時に開始し、完了するのを待っています。完了したら、私は他のタスクを実行したい。私はこれについて、リソースの束を読んで、私はここで 並行してタスクを実行する

が(コードが簡素化されました)私は現在持っているものです...右私の特定のケースのためにそれを得ることができません。

public async Task RunTasks(){ 
var tasks = new List<Task> 
{ 
    new Task(async() => await DoWork()), 
    //and so on with the other 9 similar tasks 
} 

Parallel.ForEach(tasks, task => 
{ 
    task.Start(); 
}); 

Task.WhenAll(tasks).ContinueWith(done=>{ 
    //Run the other tasks 
}); 
} 

//This function perform some I/O operations 
public async Task DoWork(){ 
    var results = await GetDataFromDatabaseAsync(); 
    foreach(var result in results){ 
     await ReadFromNetwork(result.Url); 
    } 
} 

だから、私の問題WhenAllコールでタスクが完了するのを待っているときに、完了していないのにすべてのタスクが終了していることがわかります。私はforeachConsole.WriteLineを追加しようとしました。私は継続タスクを入力したとき、データは以前のTaskから入ってきて、実際には終了しません。

私はここで間違っていますか?

+0

は、私はあなたが持っているすべての本に疑う 'タスク'どこか(タスクのコンストラクタは直接やや疑わしい呼び出し)とあなたが本当に内部の作業を待つしたいです。 Taskコンストラクタ/ Startの代わりに 'Task.Run'を使用できますか? 'Task.Run'はアンラップを行います。 –

+0

は、だから、私がやるべきだと思う: VARタスク=新しいリスト { Task.Run(()=> DoWork()) } –

答えて

16

ほとんどの場合、Taskコンストラクタは直接使用しないでください。あなたの場合、タスクはあなたが待つことができない実際のタスクだけを起動します。

DoWorkを呼び出してタスクを取得し、リストに保存してすべてのタスクが完了するまで待つことができます。意味:

tasks.Add(DoWork()); 
// ... 
await Task.WhenAll(tasks); 

ただし、非同期メソッドは、完了していないタスクが最初に待機するまで同期して実行されます。あなたはその部分は時間がかかりすぎて心配している場合、別のThreadPoolスレッドにそれをオフロードするTask.Runを使用し、リスト内そのタスク格納します。

tasks.Add(Task.Run(() => DoWork())); 
// ... 
await Task.WhenAll(tasks); 
+0

を*最初のawaitまで、同期的に実行*あなたは、彼らが実際に希望を意味したくなかったことで'Task.WhenAll'を呼び出したときだけ非同期を実行しますか?ありがとう! – Alisson

+0

@Alisson私はDoWorkの同期部分を参照していましたが、それを呼び出すコードではありませんでした。 – i3arnon

0

DoWork方法は、非同期I/O方式であるが。つまり、メソッドがI/Oが完了するまで非同期に待機するため、複数のスレッドを実行する必要はありません。 1つのスレッドで十分です。

public async Task RunTasks() 
{ 
    var tasks = new List<Task> 
    { 
     DoWork(), 
     //and so on with the other 9 similar tasks 
    }; 

    await Task.WhenAll(tasks); 

    //Run the other tasks    
} 

新しいタスクを作成するには、ほとんどnever use the Task constructorにする必要があります。非同期I/Oタスクを作成するには、単にasyncメソッドを呼び出します。スレッドプールスレッドで実行されるタスクを作成するには、Task.Runを使用します。 Task.Runの詳細な説明とその他のタスク作成オプションについては、this articleを参照してください。あなたはTPLを使用して別のスレッドでこれらのタスクの並列に実行したい場合は

0

は、あなたがこのような何かが必要になる場合があります

public async Task RunTasks() 
{ 
    var tasks = new List<Func<Task>> 
    { 
     DoWork, 
     //... 
    }; 

    await Task.WhenAll(tasks.AsParallel().Select(async task => await task())); 

    //Run the other tasks 
} 

コードのごく少量を並列これらのアプローチ:スレッドプールへのメソッドのキューイングおよび未完了の返品はTaskです。また、このような少量のタスクでは、並列化は非同期で実行するよりも時間がかかります。これは、あなたのタスクが最初に待ち受ける前に、より長い(同期的な)仕事をする場合にのみ意味をなさけることができます。

ほとんどの場合、より良い方法は次のようになります。あなたのコードでは私の意見に

public async Task RunTasks() 
{ 
    await Task.WhenAll(new [] 
    { 
     DoWork(), 
     //... 
    }); 
    //Run the other tasks 
} 

  1. あなたはParallel.ForEachに渡す前Taskでコードをラップするべきではありません。

  2. ContinueWithではなく、awaitTask.WhenAllとすることができます。

5

本質的に、互換性のない2つの非同期パラダイムが混在しています。すなわちParallel.ForEach()およびasync-awaitである。

希望するものは、どちらか一方をお選びください。例えば。 Parallel.For[Each]()を使用して、async-awaitを完全に削除することができます。 Parallel.For[Each]()は、すべての並列タスクが完了したときにのみ戻るので、他のタスクに移動することができます。

コードは、あまりにも他のいくつかの問題があります。

  • あなたはメソッドの非同期をマークしたが(インクルードはあなたが持っている待つデリゲート、ではない方法である)、それに待つません。

  • あなたはほぼ確実に.ConfigureAwait(false)を待っています。特に、すぐに結果をUIスレッドで使用しようとしていない場合は、ご利用ください。

関連する問題