2013-06-22 44 views
80

我有以下四个测试,当我运行它的最后一挂,我的问题是,为什么出现这种情况:等待的作品,但呼吁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; } 
} 

为什么最后TES挂?

+6

你应该避免异步方法返回void。它只是为了向后兼容现有的事件处理程序,主要是在接口代码中。如果你的异步方法没有返回任何东西,它应该返回任务。我有MSTest和无效返回异步测试无数问题。 – ghord

+2

@ghord:MSTest根本不支持'async void'单元测试方法;他们根本无法工作。但是,NUnit的确如此。这就是说,我同意'async Task'优先于'async void'的一般原则。 –

+0

@StephenCleary是的,虽然它在VS2012的beta版中被允许,但它导致了各种问题。 – ghord

回答

58

你快成我描述on my blogin an MSDN article标准死锁情况:async方法试图安排其延续到正在封锁的呼吁Result一个线程。

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

+4

更改为异步任务工作,现在我需要阅读您的链接的内容几次,先生。 –

+24

由于不提供解决方案,因此只能说明问题。请参阅下面的我的评论以解决问题。 –

+2

梦幻般的答案。谢谢Stephen。 –

13

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

IRestResponse<DummyServiceStatus> response = await restResponse; 

=>

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

我已经通过描述这一缺陷在我的博客文章Pitfalls of async/await

134

获取的值异步方法:

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

Syncronously调用异步方法

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

没有死锁的问题会发生由于使用Task.Run的。

+8

-1,它鼓励使用'async void'单元测试方法,并从被测系统中去除'SynchronizationContext'提供的相同线程保证。 –

+33

@StephenCleary:没有“enouraging”异步无效。它只是使用有效的C#构造来解决死锁问题。上述代码片段是解决OP问题的一个不可或缺的简单解决方案。 Stackoverflow是针对问题的解决方案,而不是详细的自我推销。 –

+3

这显然不是“不可或缺”的,因为我的解决方案没有'async void','Task.Run','Result'或'Wait'。 –

5

您正在使用Task.Result属性阻止UI。 在MSDN Documentation他们已经明确提到,

“的结果属性是阻隔性。如果您尝试访问它 其任务完成之前,这是当前活动的线程 阻塞,直到任务在大多数 的情况下,您应该通过使用等待等待而不是 直接访问属性来访问该值。

对于这种情况,最好的解决办法是删除这两个等待&异步从方法&只使用任务在那里你返回结果。它不会混淆你的执行顺序。

1

如果在调用服务/ API异步函数后没有回调或控件挂起。 您必须配置上下文才能在相同的调用上下文中返回结果。 使用TestAsync()。ConfigureAwait(continueOnCapturedContext:false);

您只能在Web应用程序而不是在面对这个问题静态无效的主要

相关问题