2013-12-10 33 views
1

我正在使用定時web客戶端的項目。類結構就是這樣。c#爲什麼當WebClient通過線程調用時,WebClient超時大部分時間?

Controller =>主要類的主管 Form1,SourceReader,ReportWriter,UrlFileReader,HTTPWorker,TimedWebClient。

HTTPworker是在獲得url時獲取頁面源的類。 TimedWebClient是處理WebClient超時的類。這是代碼。

class TimedWebClient : WebClient 
{ 
    int Timeout; 

    public TimedWebClient() 
    { 
     this.Timeout = 5000; 
    } 


     protected override WebRequest GetWebRequest(Uri address) 
    { 
     var objWebRequest = base.GetWebRequest(address); 
     objWebRequest.Timeout = this.Timeout; 
     return objWebRequest; 
    } 
} 

在HTTPWorker我有

TimedWebClient wclient = new TimedWebClient(); 
wclient.Proxy = WebRequest.GetSystemWebProxy(); 
wclient.Headers["Accept"] = "application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*"; 
wclient.Headers["User-Agent"] = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; MDDC)"; 
string pagesource = wclient.DownloadData(requestUrl); 
UTF8Encoding objUTF8 = new UTF8Encoding(); 
responseData = objUTF8.GetString(pagesource); 

我已經有處理的異常。 在Form1中我有一個背景控制器和一個urllist。

首次執行:

首先,我花了一個網址的時間和給了它唯一的控制器對象的過程。 然後它工作正常。但是,由於它是連續的,因此列表太長時,花了很長時間。

第二實例:

然後在的BackgroundWorker的Do_Work我七次控制器和七根絲線。每個控制器都有唯一的HTTPWorker對象。但現在它拋出異常說「超時」。

以下是Form1.cs backgroundworker1_DoWork中的代碼。

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 
    bool done = false; 

    while (!backgroundWorker1.CancellationPending && !done) 
    { 
     int iterator = 1; 
     int tempiterator = iterator; 
     Controller[] cntrlrarray = new Controller[numofcontrollers]; 
     Thread[] threadarray = new Thread[numofcontrollers]; 
     int cntrlcntr = 0; 
     for (cntrlcntr = 0; cntrlcntr < numofcontrollers; cntrlcntr++) 
     { 
      cntrlrarray[cntrlcntr] = new Controller(); 

     } 
     cntrlcntr = 0; 
     for (iterator = 1; iterator <= this.urlList.Count; iterator++) 
     { 
      int assignedthreads = 0; 

      for (int threadcounter = 0; threadcounter < numofcontrollers; threadcounter++) 
      { 
       cntrlcntr = threadcounter; 
       threadarray[threadcounter] = new Thread(() => cntrlrarray[cntrlcntr].Process(iterator - 1)); 
       threadarray[threadcounter].Name = this.urlList[iterator - 1]; 
       threadarray[threadcounter].Start(); 
       backgroundWorker1.ReportProgress(iterator); 
       assignedthreads++; 

       if (iterator == this.urlList.Count) 
       { 
        break; 
       } 
       else 
       { 
        iterator++; 
       } 

      } 

      for (int threadcounter = 0; threadcounter < assignedthreads; threadcounter++) 
      { 
       cntrlcntr = threadcounter; 
       threadarray[threadcounter].Join(); 

      } 
      if (iterator == this.urlList.Count) 
      { 
       break; 
      } 
      else 
      { 
       iterator--; 
      } 

     } 
     done = true; 
    } 
} 

這是什麼原因和解決方案? Appolgises太長。先謝謝你。

+0

先用較少的線程嘗試一下。最有可能的地方有一個限制(4個同時連接)。通過MaxDegree選項而不是Thread對象來使用'Parallel.ForEach()'。 –

回答

5

天空......它充滿了線程!然而,嚴肅地說 - 不要使用這麼多線程。這就是異步I/O的用途。如果你使用的是.NET 4.5,那麼使用await/async很容易,否則它是一些樣板代碼,但它仍然比這更好。

因爲這樣,默認情況下TCP連接的數量是相當有限的。即使有一次有1000次下載的使用(它可能不是,因爲你共享帶寬),你無法創建和丟棄TCP連接 - 無限制 - 打開TCP連接是有限制的(除非你在服務器上,否則從5到20的任何地方)。你可以改變這一點,但通常更喜歡以不同的方式做事。看到這個entry。如果這個應用程序沒有單獨運行(這可能不是,因爲你在服務器Windows上沒有這樣的問題),這可能也是一個問題。例如,洪流客戶端經常遇到半開連接限制(一個連接仍在等待初始TCP handskahe結束)。當然,這對您的應用程序來說是破壞性的)。

現在,即使您保持在此限制之下,通信時也會使用固定數量的出站和入站端口。當你快速打開和關閉TCP連接時,這是一個問題,因爲TCP會在後臺保持連接約4分鐘(以確保沒有錯誤的數據包到達端口,同時可以重新使用)。這意味着如果在此時間間隔內創建足夠的連接,您將「餓死」您的端口池,並且每新的TCP連接將被拒絕(因此您的瀏覽器將暫時停止工作等)。

接下來,5秒超時很低。真。想象一下,完成一次握手需要一秒鐘的時間(這是一個約300ms的ping,這仍然在合理的互聯網響應的範圍內)。突然之間,你有了一個新的連接,必須等待其他握手才能完成,而這可能需要幾秒鐘的時間。這還只是連接的啓動。然後是DNS查找,以及HTTP服務器本身的響應...... 5秒是超時。

簡而言之,它不是多線程 - 它是你打開的大量(無用的)連接。另外,對於單個網站上的URL,您應該查看Keep-Alive連接 - 它們可以重新使用已打開的TCP連接,這可以顯着減輕此問題。

現在,深入瞭解這一點。不必要地開始和銷燬線程。相反,擁有一個URL隊列和多個線程使用者將會是一個更好的主意,它會從隊列中獲取輸入。通過這種方式,只要有些東西在裏面,就只有那些從隊列中輪詢的線程(或任何數量的線程),從而節省了大量系統資源(並提高了性能)。我在想,你所做的Thread.Join也可能與你的問題有關。即使你在後臺工作人員中運行,也可能會有一些奇怪的事情發生。

+0

非常感謝!如果你可以請給我一個示例代碼使用異步和等待。我也在嘗試。我現在明白爲什麼這不起作用。我想這樣做使用異步和等待。因爲它乾淨清晰。你能舉個例子嗎? –

+0

@HareendraChamara看看這個[回覆](http://stackoverflow.com/a/7475427/3032289)。乍看之下,這似乎是正確的方法。或者,使用現成的解決方案 - https://code.google.com/p/abot/ – Luaan

相關問題