【问题标题】:Async HttpWebRequest with no wait from within a web application在 Web 应用程序中无需等待的异步 HttpWebRequest
【发布时间】: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(无需等待),还是始终转换为同步请求?

【问题讨论】:

    标签: asp.net asynchronous 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】:

    【讨论】:

    • 这太棒了!!尤其是 haacked.com 博客如何将其缩减为一行代码。我可能最终也会在我的应用程序的其他部分使用这种模式。
    • 我想确保 ThreadPool.QueueUserWorkItem 在 IIS 中是安全的。 (相关stackoverflow.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,这实际上是一堆内部调用。它是这样的:

    1. HttpWebRequest.BeginGetResponse
    2. ServicePoint.SubmitRequest
    3. Connection.SubmitRequest
    4. 两个选项:
      • 如果连接清晰: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 卡住。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-01-30
      • 1970-01-01
      • 2015-04-09
      • 1970-01-01
      • 2020-01-09
      • 2015-02-06
      • 1970-01-01
      • 2020-12-09
      相关资源
      最近更新 更多