次の4つのテストがあり、実行すると最後のテストがハングします。私の質問は、これがなぜ起こるかです。
[Test]
public void CheckOnceResultTest()
{
Assert.IsTrue(CheckStatus().Result);
}
[Test]
public async void CheckOnceAwaitTest()
{
Assert.IsTrue(await CheckStatus());
}
[Test]
public async void CheckStatusTwiceAwaitTest()
{
Assert.IsTrue(await CheckStatus());
Assert.IsTrue(await CheckStatus());
}
[Test]
public async void CheckStatusTwiceResultTest()
{
Assert.IsTrue(CheckStatus().Result); // This hangs
Assert.IsTrue(await CheckStatus());
}
private async Task<bool> CheckStatus()
{
var restClient = new RestClient(@"https://api.test.nordnet.se/next/1");
Task<IRestResponse<DummyServiceStatus>> restResponse = restClient.ExecuteTaskAsync<DummyServiceStatus>(new RestRequest(Method.GET));
IRestResponse<DummyServiceStatus> response = await restResponse;
return response.Data.SystemRunning;
}
restsharp RestClient にこの拡張メソッドを使用します:
public static class RestClientExt
{
public static Task<IRestResponse<T>> ExecuteTaskAsync<T>(this RestClient client, IRestRequest request) where T : new()
{
var tcs = new TaskCompletionSource<IRestResponse<T>>();
RestRequestAsyncHandle asyncHandle = client.ExecuteAsync<T>(request, tcs.SetResult);
return tcs.Task;
}
}
public class DummyServiceStatus
{
public string Message { get; set; }
public bool ValidVersion { get; set; }
public bool SystemRunning { get; set; }
public bool SkipPhrase { get; set; }
public long Timestamp { get; set; }
}
最後のテストがハングするのはなぜですか?
非同期メソッドによる値の取得:
var result = Task.Run(() => asyncGetValue()).Result;
非同期メソッドの同期呼び出し
Task.Run( () => asyncMethod()).Wait();
Task.Runの使用によるデッドロックの問題は発生しません。
この行にConfigureAwait(false)
を追加するデッドロックを回避できます。
IRestResponse<DummyServiceStatus> response = await restResponse;
=>
IRestResponse<DummyServiceStatus> response = await restResponse.ConfigureAwait(false);
この落とし穴についてはブログ投稿で説明しました Pitfalls of async/await
Task.Resultプロパティを使用してUIをブロックしています。 MSDNドキュメンテーション で、彼らはそれを明確に述べました、
「Resultプロパティはブロックプロパティです。タスクが完了する前にアクセスしようとすると、現在アクティブなスレッドは、タスクが完了して値が利用可能になるまでブロックされます。ほとんどの場合、プロパティに直接アクセスする代わりに、Awaitまたはawaitを使用して値にアクセスする必要があります。
このシナリオの最適な解決策は、メソッドからawaitとasyncの両方を削除し、結果を返しているTaskのみを使用することです。実行シーケンスが混乱することはありません。
サービス/ API非同期関数を呼び出した後、コールバックまたはコントロールがハングアップしない場合。
同じ呼び出されたコンテキストで結果を返すようにコンテキストを構成する必要があります。 TestAsync().ConfigureAwait(continueOnCapturedContext: false);
を使用
この問題に直面するのはWebアプリケーションのみであり、Static void mainでは発生しません。