死鎖

2013-09-05 237 views
5

我想在我的程序,實現如下調用堆棧/工作流程:死鎖

  1. 訊()
  2. 授權()
  3. httpPost()

我的想法是,httpPost()將是異步的,而另外兩種方法仍然是非異步的。但是,由於某種原因,除非我做了2 + 3,否則它不適用於我。異步。也許我仍然有一些誤解。

據我的理解,我可以a)在調用異步方法時使用await關鍵字(這將掛起方法並在異步方法完成後繼續)或者b)省略關鍵字,而是調用Task。異步方法的結果返回值,該值將阻塞直到結果可用。


讓我告訴你的工作示例:

private int dispatch(string options) 
    { 
     int res = authorize(options).Result; 
     return res; 
    } 

    static async private Task<int> authorize(string options) 
    { 
     string values= getValuesFromOptions(options); 

     KeyValuePair<int, string> response = await httpPost(url, values); 

     return 0; 
    } 

    public static async Task<KeyValuePair<int, string>> httpPost(string url, List<KeyValuePair<string, string>> parameters) 
    { 
    var httpClient = new HttpClient(new HttpClientHandler()); 

    HttpResponseMessage response = await httpClient.PostAsync(url, new FormUrlEncodedContent(parameters)); 

    int code = (int)response.StatusCode; 

    response.EnsureSuccessStatusCode(); 

    string responseString = await response.Content.ReadAsStringAsync(); 

    return new KeyValuePair<int, string>(code, responseString); 
    } 

讓我告訴你的木材加工例如:

private int dispatch(string options) 
    { 
     int res = authorize(options).Result; 
     return res; 
    } 

    static private int authorize(string options) 
    { 
     string values= getValuesFromOptions(options); 

     Task<KeyValuePair<int, string>> response = httpPost(url, values); 

     doSomethingWith(response.Result); // execution will hang here forever 

     return 0; 
    } 

    public static async Task<KeyValuePair<int, string>> httpPost(string url, List<KeyValuePair<string, string>> parameters) 
    { 
    var httpClient = new HttpClient(new HttpClientHandler()); 

    HttpResponseMessage response = await httpClient.PostAsync(url, new FormUrlEncodedContent(parameters)); 

    int code = (int)response.StatusCode; 

    response.EnsureSuccessStatusCode(); 

    string responseString = await response.Content.ReadAsStringAsync(); 

    return new KeyValuePair<int, string>(code, responseString); 
    } 

我也試圖讓所有3種方法非異步,並替換httpPost中的await.Result S,但隨後它會永遠在該行掛HttpResponseMessage response = httpClient.PostAsync(url, new FormUrlEncodedContent(parameters)).Result;

可能有人開導我,並說明我的錯誤是什麼?

回答

11

您有一個SynchronizationContext,並且當您在await上捕獲該上下文時,以便可以在該上下文中運行延續。

您正在開始一個異步任務,計劃在稍後的某個時間點在主要環境中運行連續任務。

然後,在異步操作完成之前,您的主上下文中的代碼會在異步操作中進行阻塞等待。由於上下文忙於等待延續,因此無法安排延續。經典的僵局。

這就是爲什麼「第一步異步」很重要,正如你在第一個例子中所做的那樣。

在第二個例子中有一些可以解決死鎖的黑客,但它仍然不是你應該做的。整個異步點是爲了避免阻塞你的線程。如果你只是在阻止等待任務的時候進行,那麼你就會破壞異步的目的。要麼一切都是異步的,要麼是不同步的,除非你沒有選擇。

+0

若要測試這是否正確的解釋,採取非工作代碼並將每個「await X」更改爲「await X.ConfigureAwait(false)」。如果解釋是正確的,它現在應該工作。 另一個注意事項:如果你在ASP.Net或UI應用程序中運行,你有一個SynchronizationContext。如果是這樣,請比較在獨立控制檯項目中運行相同的代碼。 – danarmak

+1

@servy:然而,我沒有得到的是我的工作示例和非工作示例之間的實際區別。我明白你說'await'安排了一個延續,'.Result'只是普通塊(是這樣嗎?),但這不會意味着我需要一個無盡的異步等待鏈,因爲沒有'await'我會阻止/死鎖我的線程? 我的意思是第一個例子在'int res = authorize(options).Result;'中不*攔截,但第二個例子在'doSomethingWith(response.Result)中攔截;爲什麼? – user826955