2013-06-22 78 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應用程序而不是在面對這個問題靜態無效的主要

相關問題