私はそのような方法がありました:
public async Task<MyResult> GetResult()
{
MyResult result = new MyResult();
foreach(var method in Methods)
{
string json = await Process(method);
result.Prop1 = PopulateProp1(json);
result.Prop2 = PopulateProp2(json);
}
return result;
}
次に、Parallel.ForEach
を使用することにしました。
public async Task<MyResult> GetResult()
{
MyResult result = new MyResult();
Parallel.ForEach(Methods, async method =>
{
string json = await Process(method);
result.Prop1 = PopulateProp1(json);
result.Prop2 = PopulateProp2(json);
});
return result;
}
しかし、今私はエラーを持っています:
非同期操作がまだ保留中に、非同期モジュールまたはハンドラーが完了しました。
async
はForEach
ではうまく機能しません。特に、async
ラムダはasync void
メソッドに変換されています。多くの async void
を避ける理由 があります(MSDNの記事で説明しています)。それらの1つは、async
ラムダがいつ完了したかを簡単に検出できないことです。 ASP.NETは、async void
メソッドを完了せずにコードが戻ることを確認し、(適切に)例外をスローします。
おそらくやりたいことは、concurrentlyにデータを処理することです。parallel。 ASP.NETで並列コードを使用することはほとんどありません。非同期同時処理を使用した場合のコードは次のとおりです。
public async Task<MyResult> GetResult()
{
MyResult result = new MyResult();
var tasks = Methods.Select(method => ProcessAsync(method)).ToArray();
string[] json = await Task.WhenAll(tasks);
result.Prop1 = PopulateProp1(json[0]);
...
return result;
}
または、 AsyncEnumerator NuGet Package でこれを行うことができます。
using System.Collections.Async;
public async Task<MyResult> GetResult()
{
MyResult result = new MyResult();
await Methods.ParallelForEachAsync(async method =>
{
string json = await Process(method);
result.Prop1 = PopulateProp1(json);
result.Prop2 = PopulateProp2(json);
}, maxDegreeOfParallelism: 10);
return result;
}
ここで、ParallelForEachAsync
は拡張メソッドです。
ああ、大丈夫。私は今何が起こっているか知っていると思います。 async method =>
「fire and forget」である「async void」(イベントハンドラ以外にはお勧めしません)。これは、呼び出し側が完了したことを知ることができないことを意味します...したがって、GetResult
は、操作の実行中に戻ります。最初の答えの技術的な詳細は間違っていますが、結果はここでは同じです。つまり、ForEach
によって開始された操作がまだ実行されている間にGetResultが返されます。本当にできるのは、await
でProcess
を(ラムダがasync
でなくなるように)しないことと、各反復が完了するまでProcess
を待つことだけです。しかし、それは少なくとも1つのスレッドプールスレッドを使用してそれを行うため、ForEach
を無意味に使用する可能性があります。私は単にParallel.ForEachを使用しないでしょう...