2013-10-22 48 views
3

我想同步使用HttpClient,但是當我做出許多併發請求時,它停止工作。我寫了兩個測試,一個用於異步使用,一個用於同步使用。 TestMethod總是在4秒後返回響應。異步測試正常工作。幾乎所有的請求都在同步測試中超時,最後的請求只有20個成功。我嘗試使用單個HttpClient來請求併爲每個新請求創建新的HttpClient實例。沒有不同。可能它與this deadlock有關。同步併發HttpClient使用

我正在使用VS2013和.NET Framework 4.5.1,目標是Framework 4.5。我通過NuGet獲得HttpClient:<package id="Microsoft.Net.Http" version="2.2.15" targetFramework="net45" />

我不想異步使用HttpClient,因爲這意味着我必須重寫我的整個應用程序。任何想法我在這裏做錯了什麼?

// all 800 requests complete successfully 
[Test] 
public async void Asyncmethod() 
{ 
    var sw = new Stopwatch(); 
    sw.Start(); 

    var client = new HttpClient(); 
    client.Timeout = TimeSpan.FromSeconds(15); 

    var tasks = Enumerable.Range(0, 800).Select(x => Task.Run(async() => 
    { 
     try 
     { 
      var swx = new Stopwatch(); 
      swx.Start(); 
      var response = await client.GetStringAsync("http://localhost:7002/TestMethod").ConfigureAwait(false); 
      swx.Stop(); 
      Console.WriteLine(x + " " + response + " in " + swx.ElapsedMilliseconds + " ms."); 
     } 
     catch (Exception e) 
     { 
      Console.WriteLine(x + " Exception: " + e.Message); 
     } 
    })).ToArray(); 

    await Task.WhenAll(tasks); 

    sw.Stop(); 
    Console.WriteLine(sw.ElapsedMilliseconds); 
} 

// almost all of 800 requests time out 
[Test] 
public void Syncmethod() 
{ 
    var sw = new Stopwatch(); 
    sw.Start(); 

    var client = new HttpClient(); 

    var tasks = Enumerable.Range(0, 800).Select(x => Task.Run(() => 
    { 
     try 
     { 
      var swx = new Stopwatch(); 
      swx.Start(); 
      var response = client.GetStringAsync("http://localhost:7002/TestMethod"); 
      if (response.Wait(15000)) 
      { 
       swx.Stop(); 
       Console.WriteLine(x + " " + response.Result + " in " + swx.ElapsedMilliseconds + " ms."); 
      } 
      else 
      { 
       swx.Stop(); 
       Console.WriteLine(x + " timed out."); 
      } 
     } 
     catch (Exception e) 
     { 
      Console.WriteLine(x + " Exception: " + e.Message); 
     } 
    })).ToArray(); 

    foreach (var task in tasks) 
     task.Wait(60000); 

    sw.Stop(); 
    Console.WriteLine(sw.ElapsedMilliseconds); 
} 

回答

2

是的,這看起來像是死鎖。

此代碼的工作完美

public static void Syncmethod() 
    { 
     var sw = new Stopwatch(); 
     sw.Start(); 

     var client = new HttpClient(); 

     var tasks = Enumerable.Range(0, 800).Select(x => Task.Run(() => 
     { 

      var swx = new Stopwatch(); 
      swx.Start(); 
      var result = 
      client.GetStringAsync("http://yandex.ru").ContinueWith(task => 
      { 
       try 
       { 
        swx.Stop(); 
        Console.WriteLine(x + " " + task.Result + " in " + swx.ElapsedMilliseconds + " ms."); 
        return task.Result; 
       } 
       catch (Exception e) 
       { 
        swx.Stop(); 
        Console.WriteLine(x + " Exception: " + e.Message); 
        throw e; 
       } 
      }, TaskContinuationOptions.AttachedToParent); 

     })).ToArray(); 

     foreach (var task in tasks) 
      task.Wait(60000); 

     sw.Stop(); 
     Console.WriteLine(sw.ElapsedMilliseconds); 
    } 

我猜你需要額外的邏輯來處理超時。例如,可能是另一個在15秒內完成的任務。

看這個帖子HttpClient.GetAsync(...) never returns when using await/async

而且你上面所描述的可以通過簡單的改變

var response = client.GetStringAsync("http://yandex.ru").GetAwaiter().GetResult(); 

當你編寫代碼的異步從來沒有使用塊執行,並等待結果掛你的異步例子。

+0

首先,在您的示例中,如果請求足夠長,則任務將在請求完成之前完成。其次,我將如何編寫一個函數,使用'ContinueWith'來返回響應內容? – Thresh

+0

已更新。但是您必須重寫您的代碼才能使用任務而無需等待。 – sh1ng

7

我的確建議您使用await代替HttpClient而不是Wait。如果您確實需要同步請求,請考慮使用支持同步方法的WebClient

這就是說,我相信你看到這種行爲的原因是由於ServicePointManager.DefaultConnectionLimit,這將限制對給定服務器的請求數量。如果您想要對同一服務器發出大量併發請求(同步異步),則需要增加該限制(對於UI應用程序,默認值爲2)。

+0

'await' - 在所有問題被標記** C#-5.0 ** –

+0

問題是,如果沒有超時,則沒有任何請求完成。只有在780個因超時而取消之後,我才能完成20個已完成的請求。 – Thresh

+0

在沒有選項的情況下使用await,我將不得不重寫其他所有內容。是的,「WebClient」完美地工作。但我仍然不明白爲什麼'HttpClient'掛起。 – Thresh