1

我有一個在可移植類庫中製作的API,它需要聯繫特定於平臺的API來發送HTTP請求。這是我寫的做的WinRT的HTTP POST方法:爲什麼HttpClient在這裏出現死鎖?

任何這種壓力下把時雖然
public bool Post(IEnumerable<KeyValuePair<string, string>> headers, string data) 
    { 
     bool success = false; 
     HttpClient client = new HttpClient(new HttpClientHandler {AllowAutoRedirect = false}); 
     foreach (var header in headers) 
     { 
      client.DefaultRequestHeaders.Add(header.Key, header.Value); 
     } 

     try 
     { 
      var task=client.PostAsync(endpoint, new StringContent(data, Encoding.UTF8, "text/xml")).ContinueWith(postTask => 
        { 
         try 
         { 
          postTask.Wait(client.Timeout); //Don't wait longer than the client timeout. 
          success = postTask.Result.IsSuccessStatusCode; 
         }catch {} 
        }, TaskContinuationOptions.LongRunning); 
      task.ConfigureAwait(false); 
      task.Wait(client.Timeout); 

     } 
     catch 
     { 
      success = false; 
     } 

     return success; 
    } 

這雖然顯示出一個有趣的問題。它似乎在內部陷入僵局。就像我創建5個線程併發送POST請求一樣,這個方法將會到達它將要執行的地方沒有任何但超時。內容永遠不會到達服務器,並且.Continue代碼永遠不會執行。但是,如果我連續運行它,或者甚至可能使用2或3個線程,它將工作正常。看來,在它拋出了更多的線程,雖然使性能成倍糟糕

究竟是什麼我做錯了嗎?

+0

你知道關於你要呼叫的端點上的性能監視器的任何信息嗎?什麼是網絡服務器的平均響應時間以及您的本地超時時間有多短?可能是在負載下,性能會受到影響,超時會在請求返回之前觸發。 – cgotberg

+0

@cgotberg我用幾臺不同的服務器測試過它。我的主要「愚蠢」測試服務器只是webrick。我還使用了其他使用IIS等的服務器。最大的問題是,即使在觀看Fiddlr時,我也可以看到沒有任何交流發生。在實際發送任何東西之前,它被鎖定在某個地方 – Earlz

回答

2

我不認爲這是你的問題是,但它可能是,它真的很容易實現並測試它。默認情況下,Windows會將最大網絡連接數設置爲2,並且您可以在連接池上鎖定多於2個線程。您可以添加到您的應用程序配置

<system.net> 
    <connectionManagement> 
    <add address="*" maxconnection="300" /> 
    </connectionManagement> 
</system.net> 

或代碼,你可以做到這一點

ServicePointManager.DefaultConnectionLimit = 300 

我還考慮註釋掉繼續等待。我不認爲這是必要的。

try 
{ 
    //Comment this line out your handling it in the outside task already 
    //postTask.Wait(client.Timeout); //Don't wait longer than the client timeout. 
    success = postTask.Result.IsSuccessStatusCode; 
}catch {} 

最後,如果上述兩件事情不起作用,我會嘗試註釋掉這段代碼。

//Task.ConfigureAwait(false); 

這可能是Task.Wait加上設置Task.ConfigureAwait(假)的組合導致某種僵局,但我爲什麼沒有專家。我只知道我有一些運行多線程的非常類似的代碼,並且我的代碼中沒有Task.ConfigureAwait(false),主要是因爲我嘗試了HttpClient庫,但沒有升級到.NET 4.5所以等待不可用。

+0

'ServicePointManager.DefaultConnectionLimit = 300'經過幾個小時的調試後爲我解決了難題。這應該有更多upvotes – mode777

0

這裏的一些事情,伸出我當前的代碼:

  • ContinueWith隊列的委託當任務完成運行。所以沒有必要等待它。在這裏不需要
  • LongRunning;它會降低性能,因爲你的延續速度非常快,而且運行時間不長。
  • ConfigureAwait是沒有意義的,因爲沒有await(和返回值被丟棄反正)。
  • 超時並不需要傳遞到Task.Wait因爲任務將是超時後已經完成反正。

我有一個便攜式類庫這就需要深入到特定於平臺的API來發送HTTP請求做出的API。

我建議讓您的API異步,因爲它做HTTP。如果您想在PCL中支持完整的async/await,則可以使用Microsoft.Bcl.Async

public async Task<bool> Post(IEnumerable<KeyValuePair<string, string>> headers, string data) 
{ 
    HttpClient client = new HttpClient(new HttpClientHandler {AllowAutoRedirect = false}); 
    foreach (var header in headers) 
    { 
     client.DefaultRequestHeaders.Add(header.Key, header.Value); 
    } 

    try 
    { 
     var result = await client.PostAsync(endpoint, new StringContent(data, Encoding.UTF8, "text/xml")).ConfigureAwait(false); 
     return result.IsSuccessStatusCode; 
    } 
    catch 
    { 
     return false; 
    } 
} 
+0

不幸的是,我們不能。這是實現一個接口,必須與.Net 4.5代碼一起工作(並且我們不能爲那些.Net 4引入那個異步庫)。此外,這種傳輸發生在另一個線程上*無論如何*所以我們不會通過使其成爲除了更簡潔的代碼之外的異步來獲得任何東西 – Earlz

+0

@Earlz:'Microsoft.Bcl.Async'爲.NET 4和PCL提供'async' 。 –

+0

這仍然不能解決我們不能在框架外使用庫的要求:( – Earlz