await works but calling task.Result hangs/deadlocks

匿名 (未验证) 提交于 2019-12-03 02:09:01

问题:

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:

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?

回答1:

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

In this case, your SynchronizationContext is the one used by NUnit to execute async void test methods. I would try using async Task test methods instead.



回答2:

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.



回答3:

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

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



回答4:

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

"The Result property 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 Await or await instead of accessing the property directly."

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



回答5:

If you are getting no callbacks or control hangs up, after calling the Service/API Async Function. You have to configure Context to return result on the same called context. Use TestAsync().ConfigureAwait(continueOnCapturedContext: false);

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



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!