C# await 有效,但调用 task.Result 挂起/死锁

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17248680/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 08:57:25  来源:igfitidea点击:

await works but calling task.Result hangs/deadlocks

c#nunittaskdeadlockasync-await

提问by Johan Larsson

I have the following four tests and the last one hangs when I run it, my question is why this happens:

我有以下四个测试,最后一个在我运行时挂起,我的问题是为什么会发生这种情况:

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


I use this extension method for restsharp RestClient:

我将此扩展方法用于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; }
}

Why does the last test hang?

为什么最后一个测试挂起?

采纳答案by Stephen Cleary

You're running into the standard deadlock situation that I describe on my blogand in an MSDN article: the asyncmethod is attempting to schedule its continuation onto a thread that is being blocked by the call to Result.

您遇到了我在我的博客MSDN 文章中描述的标准死锁情况:该async方法试图将其继续安排到被调用阻塞的线程上Result

In this case, your SynchronizationContextis the one used by NUnit to execute async voidtest methods. I would try using async Tasktest methods instead.

在这种情况下,您SynchronizationContext是 NUnit 用来执行async void测试方法的。我会尝试使用async Task测试方法。

回答by Vladimir

You can avoid deadlock adding ConfigureAwait(false)to this line:

您可以避免死锁添加ConfigureAwait(false)到这一行:

IRestResponse<DummyServiceStatus> response = await restResponse;

=>

=>

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

I've described this pitfall in my blog post Pitfalls of async/await

我在我的博客文章异步/等待的陷阱中描述了这个陷阱

回答by Herman Schoenfeld

Acquiring a value via an async method:

通过异步方法获取值:

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

Syncronously calling an async method

同步调用异步方法

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

No deadlock issues will occur due to the use of Task.Run.

不会因为使用Task.Run而出现死锁问题。

回答by Dark Knight

You are blocking the UI by using Task.Result property. In MSDN Documentationthey have clearly mentioned that,

您正在使用 Task.Result 属性阻止 UI。在MSDN 文档中,他们明确提到,

"The Resultproperty is a blocking property. If you try to access it before its task is finished, the thread that's currently active is blocked until the task completes and the value is available. In most cases, you should access the value by using Awaitor awaitinstead of accessing the property directly."

" Result属性是一个阻塞属性。如果您在其任务完成之前尝试访问它,则当前处于活动状态的线程将被阻塞,直到任务完成并且该值可用为止。在大多数情况下,您应该使用Await访问该值或等待而不是直接访问该属性。”

The best solution for this scenario would be to remove both await & asyncfrom methods & use only Taskwhere you're returning result. It won't mess your execution sequence.

这种情况的最佳解决方案是从方法中删除 await 和 async,并仅在返回结果的地方使用Task。它不会弄乱你的执行顺序。

回答by Mayank Pandit

If you are getting no callbacks or control hangs up, after calling the Service/API Async Function.

如果在调用服务/API 异步函数后没有收到回调或控制挂断。

You have to configure Context to return result on the same called context. Use TestAsync().ConfigureAwait(continueOnCapturedContext: false);

您必须配置 Context 以在同一个被调用的上下文中返回结果。用TestAsync().ConfigureAwait(continueOnCapturedContext: false);

You will be facing this issue only in Web Applications but not in Static void main

您只会在 Web 应用程序中遇到此问题,而不会在 Static void main 中遇到此问题