如何在 HttpWebRequest.BeginGetResponse 上指定超时值而不阻塞线程

发布于 2024-08-19 14:19:45 字数 1894 浏览 1 评论 0原文

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

SomeObject.WaitOne()

这再次清楚地表明它会阻塞线程。我将处于高负载环境中并且不能阻塞,但如果请求时间超过 2 秒,我还需要超时。由于无法创建和管理单独的线程池,框架中是否已有某些内容可以帮助我?

起始示例:

我想要的是一种 上的异步回调方法BeginGetResponse() 在我的超时参数到期后被调用,并有一些指示发生超时。

看似明显的 TimeOut 参数在异步调用中并不适用。 在响应返回之前,ReadWriteTimeout 参数不会发挥作用。 非专有解决方案将是更好的选择。

编辑:

这是我的想法:调用BeginGetResponse后,我用我的持续时间创建一个Timer,这就是“开始”的结束处理阶段。现在,要么请求将完成并且我的“结束”阶段将被调用,要么超时期限将到期。

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

I’m trying to issue web requests asynchronously. I have my code working fine except for one thing: There doesn’t seem to be a built-in way to specify a timeout on BeginGetResponse. The MSDN example clearly show a working example but the downside to it is they all end up with a

SomeObject.WaitOne()

Which again clearly states it blocks the thread. I will be in a high load environment and can’t have blocking but I also need to timeout a request if it takes more than 2 seconds. Short of creating and managing a separate thread pool, is there something already present in the framework that can help me?

Starting examples:

What I would like is a way for the async callback on BeginGetResponse() to be invoked after my timeout parameter expires, with some indication that a timeout occurred.

The seemingly obvious TimeOut parameter is not honored on async calls.
The ReadWriteTimeout parameter doesn't come into play until the response returns.
A non-proprietary solution would be preferable.

EDIT:

Here's what I came up with: after calling BeginGetResponse, I create a Timer with my duration and that's the end of the "begin" phase of processing. Now either the request will complete and my "end" phase will be called OR the timeout period will expire.

To detect the race and have a single winner I call increment a "completed" counter in a thread-safe manner. If "timeout" is the 1st event to come back, I abort the request and stop the timer. In this situation, when "end" is called the EndGetResponse throws an error. If the "end" phase happens first, it increments the counter and the "timeout" foregoes aborting the request.

This seems to work like I want while also providing a configurable timeout. The downside is the extra timer object and the callbacks which I make no effort to avoid. I see 1-3 threads processing various portions (begin, timed out, end) so it seems like this working. And I don't have any "wait" calls.

Have I missed too much sleep or have I found a way to service my requests without blocking?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

灯下孤影 2024-08-26 14:19:45

You can to use a BackgroundWorker to run your HttpWebRequest into a separated thread, so your main thread still alive. So, this background thread will be blocked, but first one don't.

In this context, you can to use a ManualResetEvent.WaitOne() just like in that sample: HttpWebRequest.BeginGetResponse() method.

命硬 2024-08-26 14:19:45

这是一个什么样的应用程序?这是服务进程/网络应用程序/控制台应用程序吗?

您如何创建工作负载(即请求)?如果您有一个需要完成的工作队列,您可以启动“N”个异步请求(使用您构建的超时框架),然后,一旦每个请求完成(超时或成功),您就可以可以从队列中抓取下一个请求。

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

因此,如果您将应用程序配置为最多有“N”个未完成的请求,则可以维护一个在请求之间重用(而不丢弃)的“N”个计时器池。

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

)来

What kind of an application is this? Is this a service proces/ web application/console app?

How are you creating your work load (i.e requests)? If you have a queue of work that needs to be done, you can start off 'N' number of async requests (with the framework for timeouts that you have built) and then, once each request completes (either with timeout or success) you can grab the next request from the queue.

This will thus become a Producer/consumer pattern.

So, if you configure your application to have a maximum of "N' requests outstanding, you can maintain a pool of 'N' timers that you reuse (without disposing) between the requests.

Or, alternately, you can use ThreadPool.SetTimerQueueTimer() to manage your timers. The threadpool will manage the timers for you and reuse the timer between requests.

Hope this helps.

坏尐絯℡ 2024-08-26 14:19:45

看来我原来的方法是最好的方法。

Seems like my original approach is the best thing available.

金兰素衣 2024-08-26 14:19:45

如果您可以使用 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;
        }

If you can user async/await then

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;
        }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文