C# ';等待&x27;工作正常,但调用task.Result会挂起/死锁
我有以下四个测试,最后一个测试在运行时挂起。为什么会发生这种情况:C# ';等待&x27;工作正常,但调用task.Result会挂起/死锁,c#,nunit,task,deadlock,async-await,C#,Nunit,Task,Deadlock,Async Await,我有以下四个测试,最后一个测试在运行时挂起。为什么会发生这种情况: [Test] public void CheckOnceResultTest() { Assert.IsTrue(CheckStatus().Result); } [Test] public async void CheckOnceAwaitTest() { Assert.IsTrue(await CheckStatus()); } [Test] public async void CheckStatusTw
[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;
}
为什么上一个测试挂起?您遇到了我描述的标准死锁情况:
async
方法试图将其继续调度到被调用Result
阻塞的线程上
在这种情况下,NUnit使用
SynchronizationContext
执行async void
测试方法。我会尝试使用async Task
测试方法。您遇到了我描述的标准死锁情况:async
方法试图将其继续调度到被调用Result
阻塞的线程上
在这种情况下,NUnit使用
SynchronizationContext
执行async void
测试方法。我将尝试使用异步任务
测试方法。您可以避免死锁,将配置等待(false)
添加到此行:
IRestResponse<DummyServiceStatus> response = await restResponse;
IRestResponse response=wait response;
=>
IRestResponse response=wait response.configurewait(false);
我已经在我的博客文章中描述了这个陷阱您可以避免死锁将
ConfigureAwait(false)
添加到此行:
IRestResponse<DummyServiceStatus> response = await restResponse;
IRestResponse response=wait response;
=>
IRestResponse response=wait response.configurewait(false);
我在我的博客文章中描述了这个陷阱,通过异步方法获取值:
var result = Task.Run(() => asyncGetValue()).Result;
同步调用异步方法
Task.Run( () => asyncMethod()).Wait();
使用Task.Run不会出现死锁问题。通过异步方法获取值:
var result = Task.Run(() => asyncGetValue()).Result;
同步调用异步方法
Task.Run( () => asyncMethod()).Wait();
使用Task.Run不会出现死锁问题。您正在使用Task.Result属性阻止UI。 他们在报告中明确提到, “Result属性是一个阻塞属性。如果您试图访问它 在其任务完成之前,当前处于活动状态的线程 在任务完成且值可用之前被阻止。在大多数情况下 在这种情况下,应使用Await或Await而不是 直接访问该属性。”
此场景的最佳解决方案是从方法中同时删除wait和async,只在返回结果的地方使用Task。它不会扰乱您的执行顺序。您正在使用Task.Result属性阻塞UI。 他们在报告中明确提到, “Result属性是一个阻塞属性。如果您试图访问它 在其任务完成之前,当前处于活动状态的线程 在任务完成且值可用之前被阻止。在大多数情况下 在这种情况下,应使用Await或Await而不是 直接访问该属性。”
此场景的最佳解决方案是从方法中同时删除wait和async,只在返回结果的地方使用Task。它不会打乱您的执行顺序。如果您没有收到任何回调或控件挂起,在调用服务/API异步函数后,您必须配置上下文以在相同的被调用上下文上返回结果 使用
TestAsync().ConfigureAwait(continueOnCapturedContext:false)代码>
您将只在web应用程序中遇到此问题,而不是在static void main
中遇到此问题。如果您没有收到任何回调或控件挂起,则在调用service/API async函数后,您必须配置上下文以返回同一调用上下文的结果
使用TestAsync().ConfigureAwait(continueOnCapturedContext:false)代码>
您将只在web应用程序中遇到此问题,而不是在@hermanschoonfeld给出的答案的附加部分中遇到此问题。不幸的是,下面的引语不正确:
使用Task.Run不会出现死锁问题
执行被包装在Task.Run中,这将在线程池上调度任务并阻止调用线程。这没关系,只要调用线程不是线程池线程。如果调用线程来自线程池,则会发生以下灾难:一个新任务将排队到队列的末尾,而最终将执行该任务的线程池线程将被阻塞,直到该任务执行为止
在库代码中没有简单的解决方案,因为您无法假定在什么上下文下调用代码。最好的解决方案是只从异步代码中调用异步代码,阻止同步方法中的同步API,不要混合使用它们
资料来源:
对@hermanschoonfeld给出的答案的补充。不幸的是,下面的引语不正确:
使用Task.Run不会出现死锁问题
执行被包装在Task.Run中,这将在线程池上调度任务并阻止调用线程。这没关系,只要调用线程不是线程池线程。如果调用线程来自线程池,则会发生以下灾难:一个新任务将排队到队列的末尾,而最终将执行该任务的线程池线程将被阻塞,直到该任务执行为止
在库代码中没有简单的解决方案,因为您无法假定在什么上下文下调用代码。最好的解决方案是只从异步代码中调用异步代码,阻止同步方法中的同步API,不要混合使用它们
资料来源:
您应该避免从异步方法返回void。它仅用于与现有事件处理程序(主要是在接口代码中)的向后兼容性。如果您的异步方法不返回任何内容,它应该返回任务。我有很多公关
public String GetSqlConnString(RubrikkUser user, RubrikkDb db)
{
// deadlock if called from threadpool,
// works fine on UI thread, works fine from console main
return Task.Run(() =>
GetSqlConnStringAsync(user, db)).Result;
}