web-dev-qa-db-ja.com

awaitは機能しますが、タスクを呼び出します。結果がハング/デッドロックします。

次の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; }
}

最後のテストがハングするのはなぜですか?

113
Johan Larsson

私のブログで および MSDNの記事で で説明している標準的なデッドロック状態に陥っています:asyncメソッドは、 Resultへの呼び出しによってブロックされているスレッド。

この場合、SynchronizationContextは、NUnitがasync voidテストメソッドを実行するために使用するものです。代わりにasync Taskテストメソッドを使用してみます。

80
Stephen Cleary

非同期メソッドによる値の取得:

var result = Task.Run(() => asyncGetValue()).Result;

非同期メソッドの同期呼び出し

Task.Run( () => asyncMethod()).Wait();

Task.Runの使用によるデッドロックの問題は発生しません。

198

この行にConfigureAwait(false)を追加するデッドロックを回避できます。

IRestResponse<DummyServiceStatus> response = await restResponse;

=>

IRestResponse<DummyServiceStatus> response = await restResponse.ConfigureAwait(false);

この落とし穴についてはブログ投稿で説明しました Pitfalls of async/await

16
Vladimir

Task.Resultプロパティを使用してUIをブロックしています。 MSDNドキュメンテーション で、彼らはそれを明確に述べました、

Resultプロパティはブロックプロパティです。タスクが完了する前にアクセスしようとすると、現在アクティブなスレッドは、タスクが完了して値が利用可能になるまでブロックされます。ほとんどの場合、プロパティに直接アクセスする代わりに、Awaitまたはawaitを使用して値にアクセスする必要があります。

このシナリオの最適な解決策は、メソッドからawaitとasyncの両方を削除し、結果を返しているTaskのみを使用することです。実行シーケンスが混乱することはありません。

8
Dark Knight

サービス/ API非同期関数を呼び出した後、コールバックまたはコントロールがハングアップしない場合。

同じ呼び出されたコンテキストで結果を返すようにコンテキストを構成する必要があります。 TestAsync().ConfigureAwait(continueOnCapturedContext: false);を使用

この問題に直面するのはWebアプリケーションのみであり、Static void mainでは発生しません。

2
Mayank Pandit