[C#] ‘await’가 작동하지만 task.Result 호출이 중단 / 교착 상태

다음 네 가지 테스트가 있으며 마지막 테스트는 실행할 때 중단됩니다. 왜 이런 일이 발생합니까?

[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; }
}

마지막 테스트가 중단되는 이유는 무엇입니까?



답변

내 블로그MSDN 기사에서 설명하는 표준 교착 상태 상황에 처해 있습니다 .이 async메서드는 .NET에 대한 호출에 의해 차단되는 스레드에 대한 연속 작업을 예약하려고합니다 Result.

이 경우 SynchronizationContextNUnit에서 async void테스트 메서드 를 실행하는 데 사용하는 것은 your 입니다 . async Task대신 테스트 방법을 사용해 보겠습니다 .


답변

비동기 메서드를 통해 값 획득 :

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 문서 그들은 분명히 그것을 언급

결과 속성은 차단 속성입니다. 작업이 완료되기 전에 액세스하려고하면 작업이 완료되고 값을 사용할 수있을 때까지 현재 활성화 된 스레드가 차단됩니다. 대부분의 경우 Await 를 사용하여 값에 액세스해야합니다. 또는 속성에 직접 액세스하는 대신 기다 립니다. “

이 시나리오에 대한 최상의 솔루션 은 메서드에서 await 및 async모두 제거하고 결과를 반환하는 작업 만 사용 하는 것입니다. 실행 순서를 엉망으로 만들지 않습니다.


답변

콜백을받지 못하거나 컨트롤이 중단되면 서비스 / API 비동기 함수를 호출 한 후 동일한 호출 된 컨텍스트에서 결과를 반환하도록 컨텍스트를 구성해야합니다.

사용하다 TestAsync().ConfigureAwait(continueOnCapturedContext: false);

이 문제는 웹 응용 프로그램에서만 발생하고 static void main.


답변