以下は、問題を抱えているコードの簡略化されたバージョンです。これをコンソールアプリケーションで実行すると、期待通りに機能します。すべてのクエリは並行して実行され、すべてが完了するとTask.WaitAll()
が返されます。Task.WaitAllは、ASP.NETで待たされている複数のタスクにぶら下がっています。
ただし、このコードをWebアプリケーションで実行すると、リクエストがハングするだけです。デバッガを接続してすべてを中断すると、実行がTask.WaitAll()
で待機していることが示されます。最初のタスクは完了しましたが、他のタスクは完了しません。
私はASP.NETで動作しているときにハングする理由を理解できませんが、コンソールアプリケーションで正常に動作します。あなたがawait Task.WhenAll
を使用する必要が
public Foo[] DoWork(int[] values)
{
int count = values.Length;
Task[] tasks = new Task[count];
for (int i = 0; i < count; i++)
{
tasks[i] = GetFooAsync(values[i]);
}
try
{
Task.WaitAll(tasks);
}
catch (AggregateException)
{
// Handle exceptions
}
return ...
}
public async Task<Foo> GetFooAsync(int value)
{
Foo foo = null;
Func<Foo, Task> executeCommand = async (command) =>
{
foo = new Foo();
using (SqlDataReader reader = await command.ExecuteReaderAsync())
{
ReadFoo(reader, foo);
}
};
await QueryAsync(executeCommand, value);
return foo;
}
public async Task QueryAsync(Func<SqlCommand, Task> executeCommand, int value)
{
using (SqlConnection connection = new SqlConnection(...))
{
connection.Open();
using (SqlCommand command = connection.CreateCommand())
{
// Set up query...
await executeCommand(command);
// Log results...
return;
}
}
}
+1。私は "メインスレッド"ではなく "要求コンテキスト"という言葉を使用します。 –
@StephenClearyうん、議論の余地があった。メインスレッドはアプリケーション全体のメインスレッドではないので、引用符で囲みますが、「その要求のメインスレッド」と考えることができます。それは私の心の中で問題を考えるのに役立つ方法です。 – Servy
重要な注意点の1つは、Task.WhenAllがAggregateExceptionをスローしないことを待つことです。内側の例外の1つだけが伝搬されます。 AggregateException全体を調べる場合は、task.WhenAllから返されたタスクへの参照を格納し、Exceptionプロパティを明示的に調べる必要があります。 –