如何在 HttpWebRequest.BeginGetResponse 上指定超时值而不阻塞线程
我正在尝试异步发出网络请求。我的代码工作正常,除了一件事:似乎没有内置方法可以在 BeginGetResponse
上指定超时。 MSDN 示例清楚地显示了一个工作示例,但其缺点是它们最终都以 ,
SomeObject.WaitOne()
这再次清楚地表明它会阻塞线程。我将处于高负载环境中并且不能阻塞,但如果请求时间超过 2 秒,我还需要超时。由于无法创建和管理单独的线程池,框架中是否已有某些内容可以帮助我?
起始示例:
- http://msdn.microsoft.com/ en-us/library/ms227433(VS.100).aspx
- http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.begingetresponse.aspx
我想要的是一种 上的异步回调方法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:
- http://msdn.microsoft.com/en-us/library/ms227433(VS.100).aspx
- http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.begingetresponse.aspx
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您可以使用
BackgroundWorker
< /a> 将您的HttpWebRequest
运行到一个单独的线程中,这样您的主线程仍然处于活动状态。因此,这个后台线程将被阻塞,但第一个线程不会。在此上下文中,您可以使用
ManualResetEvent.WaitOne()
,就像该示例中的那样:HttpWebRequest.BeginGetResponse()
方法。You can to use a
BackgroundWorker
to run yourHttpWebRequest
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.这是一个什么样的应用程序?这是服务进程/网络应用程序/控制台应用程序吗?
您如何创建工作负载(即请求)?如果您有一个需要完成的工作队列,您可以启动“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.
看来我原来的方法是最好的方法。
Seems like my original approach is the best thing available.
如果您可以使用 async/await 那么
If you can user async/await then