2010-01-23 106 views
3

我試圖異步發出Web請求。我的代碼工作正常,除了一件事情:似乎沒有一種內置的方式來指定在BeginGetResponse上的超時。 MSDN的例子清楚地表明工作的例子,但缺點是它們都結束了一個如何在不阻塞線程的情況下在HttpWebRequest.BeginGetResponse上指定超時值

SomeObject.WaitOne() 

再次明確指出它阻止該線程。我將處於高負載環境,並且不能阻塞,但如果超過2秒,我還需要超時請求。沒有創建和管理單獨的線程池,是否有框架中可以幫助我的東西?

開始的例子:

我想是在BeginGetResponse()異步回調的方式來調用我的超時參數過期後,有一些跡象表明,發生超時。

看似明顯的TimeOut參數在異步調用中不受尊重。 ReadWriteTimeout參數在響應返回之前不起作用。 非專有解決方案將是更可取的。

編輯:

這就是我想出了:調用BeginGetResponse後,我創建我的持續時間Timer,這就是加工的「開始」階段的結束。現在要麼將完成請求,我的「結束」階段將被稱爲或超時期限將到期。

爲了檢測比賽並獲得單一勝利者,我呼籲以線程安全的方式增加一個「已完成」的計數器。如果「超時」是要返回的第一個事件,則中止請求並停止計時器。在這種情況下,當調用「結束」時,EndGetResponse將引發錯誤。如果「結束」階段首先發生,它會增加計數器,「超時」會放棄請求。

這似乎像我想要的那樣工作,同時也提供可配置的超時。缺點是額外的計時器對象和我無法避免的回調。我看到1-3線程處理各個部分(開始,超時,結束),所以它看起來像這樣工作。我沒有任何「等待」電話。

我是否錯過了太多的睡眠或者是否找到了一種方法來處理我的請求而不會阻塞?

int completed = 0; 

this.Request.BeginGetResponse(GotResponse, this.Request); 
this.timer = new Timer(Timedout, this, TimeOutDuration, Timeout.Infinite); 

private void Timedout(object state) 
{ 
    if (Interlocked.Increment(ref completed) == 1) 
    { 
     this.Request.Abort(); 
    } 
    this.timer.Change(Timeout.Infinite, Timeout.Infinite); 
    this.timer.Dispose(); 
} 

private void GotRecentSearches(IAsyncResult result) 
{ 
    Interlocked.Increment(ref completed); 
} 

回答

0

好像我原來的做法是最好的東西可用。

+0

你最終採取了什麼方法?這一個還是你找到了更好的方法?你有沒有得到完整的來源? – mythz 2011-01-13 02:07:04

0

您可以使用BackgroundWorkerHttpWebRequest碰上一個獨立的線程,所以你的主線程仍然活着。所以,這個後臺線程將被阻止,但第一個不會。

在這種情況下,您可以使用ManualResetEvent.WaitOne(),就像在該示例中那樣:HttpWebRequest.BeginGetResponse()方法。

+0

正如我的問題所指出的,WaitOne塊。我需要一個非阻塞解決方案。 – 2010-01-23 17:37:36

+0

是的,它會阻止當前線程。這就是爲什麼我建議在'BackgroundThread'中運行它。 – 2010-01-23 18:29:08

+0

如果你這樣做,它仍然在使用ThreadPool線程。看到我最新的編輯。 – 2010-01-23 18:50:14

0

這是什麼樣的應用程序?這是一個服務過程/ Web應用程序/控制檯應用程序嗎?

你是如何創建你的工作負載(即請求)?如果你有一個需要完成的工作隊列,你可以從'N'個異步請求開始(使用你構建的超時框架),然後,一旦每個請求完成(無論是超時還是成功),你可以從隊列中獲取下一個請求。

這將因此成爲生產者/消費者模式。

因此,如果您配置您的應用程序有一個最大的N個「的要求突出,可以保持池‘的要求之間,你重複使用(不配置)N’定時器。

或者,交替,你可以使用ThreadPool.SetTimerQueueTimer()來管理你的定時器。線程池將管理定時器爲你和重用請求之間的計時器。

希望這有助於。

+0

這是一個前10位的網站,邏輯運行在每頁上的速率超過9K rps。阻止任何地方不是一種選擇。 – 2010-01-24 16:49:54

+0

如果我正確理解這一點,那麼對於ASPX頁面接收到的每個請求,您將發出一個(或多個)異步HTTPWebRequests? 如果是這樣,那麼您可以利用ASPX中的任何異步模式?如果有的話,你可以啓動異步webrequest,並完成觸發完成頁面。如果超時,您還可以使用錯誤代碼或其他內容觸發頁面完成。這樣你就不會拿着一個線程池線程。 – feroze 2010-01-24 18:28:25

0

如果你可以用戶異步/等待然後

private async Task<WebResponse> getResponseAsync(HttpWebRequest request) 
     { 
      var responseTask = Task.Factory.FromAsync(request.BeginGetResponse, ar => (HttpWebResponse)request.EndGetResponse(ar), null); 
      var winner = await (Task.WhenAny(responseTask, Task.Delay(new TimeSpan(0, 0, 20)))); 
      if (winner != responseTask) 
      { 
       throw new TimeoutException(); 
      } 
      return await responseTask; 
     } 
相關問題