如何在不阻塞线程的情况下在HttpWebRequest.BeginGetResponse上指定超时值

时间:2010-01-23 07:32:19

标签: iis asynchronous httpwebrequest timeout

我正在尝试异步发出Web请求。我的代码工作正常,除了一件事:似乎没有一种内置的方法来指定BeginGetResponse的超时。 MSDN示例清楚地显示了一个工作示例,但它的缺点是它们都以

结尾
SomeObject.WaitOne()

这再次清楚地表明它阻止线程。我将处于高负载环境中并且无法阻塞,但如果需要超过2秒,我还需要超时请求。如果没有创建和管理单独的线程池,那么框架中是否存在可以帮助我的东西?

开始示例:

我想要的是在超时参数到期后调用BeginGetResponse()上的异步回调的方法,并指出发生了超时。

在异步调用中,看似明显的TimeOut参数不受尊重。 在响应返回之前,ReadWriteTimeout参数不起作用。 非专有解决方案更可取。

修改

以下是我提出的问题:在致电BeginGetResponse之后,我创建了一个Timer,其持续时间是处理“开始”阶段的结束。现在请求将完成并且我的“结束”阶段将被调用或者超时期限将到期。

为了检测比赛并拥有一名获胜者,我打电话以线程安全的方式递增一个“已完成”的计数器。如果“timeout”是第一个返回的事件,我中止请求并停止计时器。在这种情况下,当调用“end”时,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);
}

4 个答案:

答案 0 :(得分:0)

您可以使用BackgroundWorkerHttpWebRequest运行到一个单独的线程中,这样您的主线程仍然有效。所以,这个后台线程将被阻止,但第一个不会。

在这种情况下,您可以像使用HttpWebRequest.BeginGetResponse()方法一样使用ManualResetEvent.WaitOne()

答案 1 :(得分:0)

这是什么类型的应用程序?这是一个服务进程/ Web应用程序/控制台应用程序吗?

您如何创建工作量(即请求)?如果你有一个需要完成的工作队列,你可以开始'N'个异步请求(使用你构建的超时框架)然后,一旦每个请求完成(无论是超时还是成功)你可以从队列中获取下一个请求。

这将成为生产者/消费者模式。

因此,如果您将应用程序配置为最多有“N”个未完成请求,则可以维护一个“N”个定时器池,您可以在这些请求之间重复使用(不需要处理)。

或者,您也可以使用ThreadPool.SetTimerQueueTimer()来管理您的计时器。线程池将为您管理计时器,并在请求之间重用计时器。

希望这有帮助。

答案 2 :(得分:0)

似乎我原来的方法是最好的方法。

答案 3 :(得分:0)

如果您可以使用async / await那么

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;
        }