2017-04-13 86 views
0

我注意到當我使用一個單一的靜態實例HttpClient,我的代碼掛起,並退出幾分鐘後出現錯誤 - 只是說其中一個任務被取消,沒有給出具體的錯誤。多次調用靜態httpclient掛起控制檯應用程序

static HttpClient _client = new HttpClient(); 
    static void Main(string[] args) 
    { 
     try 
     { 
      var t1 = _client.GetStreamAsync("http://www.w3.org/TR/PNG/iso_8859-1.txt"); 
      t1.Wait(); 
      Console.WriteLine("complete"); 
      var t2 = _client.GetStreamAsync("http://www.w3.org/TR/PNG/iso_8859-1.txt"); 
      t2.Wait(); 
      Console.WriteLine("complete"); 
      var t3 = _client.GetStreamAsync("http://www.w3.org/TR/PNG/iso_8859-1.txt"); 
      t3.Wait(); 
      Console.WriteLine("complete"); 
      var t4 = _client.GetStreamAsync("http://www.w3.org/TR/PNG/iso_8859-1.txt"); 
      t4.Wait(); 
      Console.WriteLine("complete"); 
      Console.ReadKey(); 

     } 
     catch (System.Exception ex) 
     { 

     } 

    } 

當我切換到下面的代碼並創建單獨的HttpClient實例時,問題就消失了。

static void Main(string[] args) 
    { 
     try 
     { 
      HttpClient client1 = new HttpClient(); 
      var t1 = client1.GetStreamAsync("http://www.w3.org/TR/PNG/iso_8859-1.txt"); 
      t1.Wait(); 
      Console.WriteLine("complete"); 
      HttpClient client2 = new HttpClient(); 
      var t2 = client2.GetStreamAsync("http://www.w3.org/TR/PNG/iso_8859-1.txt"); 
      t2.Wait(); 
      Console.WriteLine("complete"); 
      HttpClient client3 = new HttpClient(); 
      var t3 = client3.GetStreamAsync("http://www.w3.org/TR/PNG/iso_8859-1.txt"); 
      t3.Wait(); 
      Console.WriteLine("complete"); 
      HttpClient client4 = new HttpClient(); 
      var t4 = client4.GetStreamAsync("http://www.w3.org/TR/PNG/iso_8859-1.txt"); 
      t4.Wait(); 
      Console.WriteLine("complete"); 
      Console.ReadKey(); 
     } 
     catch (System.Exception ex) 
     { 

     } 

    } 

我想先知道爲什麼發生這種情況,如果有一種方法可以安全地使用靜態的HttpClient多次,我在第一個代碼塊(沒有必要的,只是出於好奇)

回答

1

問題是,您使用「GetStreamAsync」方法。如果你用一個實例調用這個方法是多重的,那​​麼「_client」會打開相同的數據流並將這個數據流複製到「t1」,「t2」,...你用這個打開的流創建一個死鎖。你應該使用「GetStringAsync」,或者你應該關閉流「t1」,然後在「_client」爲「t2」打開一個流等等。下面的代碼演示,你怎麼可以這樣做:

class Program 
{ 
    private static HttpClient _client = new HttpClient(); 

    static void Main(string[] args) 
    { 
     try 
     { 
      using (var t1 = _client.GetStreamAsync("http://www.w3.org/TR/PNG/iso_8859-1.txt")) 
      { 
       t1.Wait(); 
       Console.WriteLine("complete"); 
      } 

      using (var t2 = _client.GetStreamAsync("http://www.w3.org/TR/PNG/iso_8859-1.txt")) 
      { 
       t2.Wait(); 
       Console.WriteLine("complete"); 
      } 

      using (var t3 = _client.GetStreamAsync("http://www.w3.org/TR/PNG/iso_8859-1.txt")) 
      { 
       t3.Wait(); 
       Console.WriteLine("complete"); 
      } 

      using (var t4 = _client.GetStreamAsync("http://www.w3.org/TR/PNG/iso_8859-1.txt")) 
      { 
       t4.Wait(); 
       Console.WriteLine("complete"); 
      } 

      Console.ReadKey(); 

     } 
     catch (System.Exception ex) 
     { 

     } 
    } 

我添加了一個簡單的使用,自動調用「T1」的Dispose方法,如果PROGRAMM離開「T1」的使用塊。

此外,我建議您使用「await」以便使用異步代碼乾淨地工作。這個問題,你不能在你的代碼中使用「等待」,是不能使用「Main」方法作爲異步方法。但是你可以解決這個問題,以創建像一個新的異步void方法上以下內容:

class Program 
{ 
    private static HttpClient _client = new HttpClient(); 

    static void Main(string[] args) 
    { 

     GetStreamsAsync(); 
     Console.ReadKey(); 
    } 

    static async void GetStreamsAsync() 
    { 
     try 
     { 
      using (var t1 = await _client.GetStreamAsync("http://www.w3.org/TR/PNG/iso_8859-1.txt")) 
      { 
       Console.WriteLine("complete"); 
      } 

      using (var t2 = await _client.GetStreamAsync("http://www.w3.org/TR/PNG/iso_8859-1.txt")) 
      { 
       Console.WriteLine("complete"); 
      } 

      using (var t3 = await _client.GetStreamAsync("http://www.w3.org/TR/PNG/iso_8859-1.txt")) 
      { 
       Console.WriteLine("complete"); 
      } 

      using (var t4 = await _client.GetStreamAsync("http://www.w3.org/TR/PNG/iso_8859-1.txt")) 
      { 
       Console.WriteLine("complete"); 
      } 
     } 
     catch (System.Exception ex) 
     { 

     } 
    } 
} 

你要調用「主」方法「Console.ReadKey」的方法,因爲「主」的方法完成在新的「async void GetSteamsAsync」方法之前。

更新(感謝Stuart的評論): 如果您使用其他應用程序模型,建議您也使用「ConfigureAwait」。在控制檯應用程序中使用「ConfigureAwait」沒有意義,因爲沒有SynchronizationContext來捕獲並繼續。

+1

不錯。在控制檯應用程序中,'ConfigureAwait(false)'建議是多餘的,因爲沒有SynchronizationContext需要捕獲並繼續。在其他應用程序模型中,這是至關重要的建議,或者在圖書館的良好實踐中。 – Stuart

+1

你是對的!感謝您的改進。 –

+0

第一個變體遇到了我最初的相同問題,它完成第一個和第二個任務,然後掛起一兩分鐘,拋出異常。第二種變化是完美的。我認爲第一個更像'使用(HttpClient _client = new HttpClient()) {t1} = _client.GetStreamAsync(「http://www.w3.org/TR/PNG/iso_8859-1.txt」 ); t1.Wait(); Console.WriteLine(「complete」); }' – erotavlas

相關問題