在 Web 应用程序中无需等待的异步 HttpWebRequest
Posted
技术标签:
【中文标题】在 Web 应用程序中无需等待的异步 HttpWebRequest【英文标题】:Async HttpWebRequest with no wait from within a web application 【发布时间】:2011-01-11 19:25:42 【问题描述】:在我的 Web 应用程序 (ASP.NET) 中,我有一段代码使用 HttpWebRequest 调用 REST 服务并继续执行。现在,完成整个网络请求所需的时间比我想的要长。问题是 REST 服务返回的内容没有用。理想情况下,我想向 REST 服务发送一个异步 Web 请求,然后不等待响应。问题是我已经尝试过使用
request.BeginGetResponse(New AsyncCallback(AddressOf myFunc), Nothing)
要启动异步请求而不是等待(我认为这是异步请求的默认行为),它会在执行BeginGetResponse
之后的下一行代码之前连续执行回调函数。
我怀疑 ASP.NET 在 Web 应用程序中可能会将其转换为同步请求。我之所以相信这一点,是因为有一个IAsyncResult result
对象被传递到回调函数中,当我检查它的CompletedSynchronously
属性时,它总是设置为true。
有谁知道是否可以从 ASP.NET Web 应用程序中执行异步 HttpWebRequest(无需等待),还是始终转换为同步请求?
【问题讨论】:
【参考方案1】:HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(myUrl);
//set up web request...
ThreadPool.QueueUserWorkItem(o=> myRequest.GetResponse(); );
也称为“一劳永逸”。
【讨论】:
ThreadPool
现在我会更频繁地使用它 :) 这让火灾和遗忘变得如此简单...... +1
更新,.NET 4.0 的更现代的方法可能是 Task.Factory.StartNew(() => myRequest.GetResponse());
甚至Task.Run(() => ...)【参考方案2】:
您可能正在查看“一劳永逸”的模式。这里有一些链接。
http://weblogs.asp.net/albertpascual/archive/2009/05/14/fire-and-forget-class-for-asp-net.aspx
http://haacked.com/archive/2009/01/09/asynchronous-fire-and-forget-with-lambdas.aspx
http://www.eggheadcafe.com/articles/20060727.asp
希望对你有帮助
【讨论】:
这太棒了!!尤其是 haacked.com 博客如何将其缩减为一行代码。我可能最终也会在我的应用程序的其他部分使用这种模式。 我想确保 ThreadPool.QueueUserWorkItem 在 IIS 中是安全的。 (相关***.com/questions/1325718/…)【参考方案3】:(注意:我开始输入这个作为评论,但随着我在研究过程中获得了一些额外的知识,它变得足够大,可以回答)
1。 ThreadPool.QueueUserWorkItem
@ram 提供的前两个链接以及@Bryan Batchelders 的回答利用了有争议的 ThreadPool.QueueUserWorkItem。正如@Perhentian link 所示,这在 ASP.NET 环境中是有争议的,因为不小心使用可能使您的线程池饿死。
2。开始调用
然后我查看了@ram 的third link,它使用了BeginInvoke。从本质上讲,这似乎只是告诉一些代码也在另一个线程上运行。所以这里没有解决方案。
3。开始获取响应
现在回到@Perhentians link。它说明 BeginGetResponse 与实际线程有何不同,因为它使用 IO 完成端口 (IOPC)。因此,您实际上正在寻找的是一种仍然使用这些 IOPC 而不创建额外线程的解决方案。
现在,为了了解 .NET 是如何做到这一点的,我尝试深入研究 HttpWebRequest.BeginGetResponse,这实际上是一堆内部调用。它是这样的:
-
HttpWebRequest.BeginGetResponse
ServicePoint.SubmitRequest
Connection.SubmitRequest
两个选项:
如果连接清晰:Connection.StartRequest
否则:Connection.WaitList.Add(request)
A.候补名单
首先让我们考虑选项 2:当与先前请求的连接完成时,将处理所述 WaitList。看一下整个链中涉及的 ConnectStream 会发现一个 ThreadPool.QueueUserWorkItem 带有备注:
// otherwise we queue a work item to parse the chunk
// Consider: Will we have an issue of thread dying off
// if we make a IO Read from that thread???
因此,至少在某些回退方案中,线程仍然可以通过使用 BeginGetResponse 无意中被框架生成!
B. Connection.StartRequest
现在,我们仍然有连接清晰的场景。回调由 System.Net.Sockets.Socket.BeginConnect 安装,并由 BeginAccept 实际调用。更多挖掘揭示了对 ThreadPool.UnsafeRegisterWaitForSingleObject 的调用,其结果用于等待。
结论
最后,我们可以知道在执行 BeginGetResponse 时实际发生了什么:
// 1. Connecting a socket
UnsafeNclNativeMethods.OSSOCK.WSAConnect(m_handle)
// 2. Registering the callback
m_RegisteredWait = ThreadPool.UnsafeRegisterWaitForSingleObject(m_AsyncEvent, s_RegisteredWaitCallback, this, Timeout.Infinite, true);
// 3. Waiting for the socket to complete
UnsafeNclNativeMethods.OSSOCK.WSAEventSelect(m_handle, m_AsyncEvent.SafeWaitHandle, blockEventBits);
注意 Timeout.Infinite。我仍在调查是否有可能让套接字运行不执行等待的代码路径,但目前看来,这对我来说是不可能的。
正如我的结论:在“即发即弃”场景中使用 IOCP 似乎没有简单的方法。因此,除非您可以让上述套接字不等待 BeginAccept 完成,否则您将被 ThreadPool 卡住。
【讨论】:
以上是关于在 Web 应用程序中无需等待的异步 HttpWebRequest的主要内容,如果未能解决你的问题,请参考以下文章
如何在 ASP.NET 中等待异步 Web 服务调用的结果以获得最佳性能
.NET Core: 用 Fire & forget 模式执行无需等待的异步操作
.NET Core: 用 Fire & forget 模式执行无需等待的异步操作