等待工作,但调用task.Result挂起/死锁
我有以下四个testing,最后一个挂起时,我运行它,我的问题是为什么会发生这种情况:
[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; } }
为什么最后的testing挂起?
您遇到了我在博客和MSDN文章中描述的标准死锁情况: async
方法试图将其延续安排到正在被Result
调用阻止的线程。
在这种情况下,您的SynchronizationContext
是NUnit用来执行async void
testing方法的一个。 我会尝试使用async Task
testing方法。
通过asynchronous方法获取值:
var result = Task.Run(() => asyncGetValue()).Result;
同步调用一个asynchronous方法
Task.Run( () => asyncMethod()).Wait();
由于使用Task.Run,将不会发生死锁问题。
你可以避免死锁添加ConfigureAwait(false)
到这一行:
IRestResponse<DummyServiceStatus> response = await restResponse;
=>
IRestResponse<DummyServiceStatus> response = await restResponse.ConfigureAwait(false);
我在我的博客文章asynchronous/等待的陷阱中描述了这个陷阱
您正在使用Task.Result属性阻止UI。 在MSDN文档中他们明确提到,
“ Result属性是一个阻塞属性,如果在任务完成之前尝试访问它,当前活动的线程将被阻塞,直到任务完成并且值可用。在大多数情况下,您应该通过使用” 等待“来访问该值或者等待直接访问该属性。“
这种情况下的最佳解决scheme是从方法中删除await&async ,并在返回结果时只使用Task 。 这不会搅乱你的执行顺序。
如果您没有获得callback或控制挂断,在调用服务/ APIasynchronous函数。 您必须configuration上下文才能在相同的调用上下文中返回结果。 使用TestAsync()。ConfigureAwait(continueOnCapturedContext:false);
您将只在Web应用程序中面对此问题,但不在Static void main中