如何:开始处理请求

3
编写在IIS上运行的服务。
基本上看起来像这样:
void ProcessRequest(HttpContext context)
{
     <Init Stuff>
     <Access DB>   // This may potentially stall for DB access
     <Write Output to conext stream>
}

通过在<Access DB>部分阻塞线程,我们基本上是阻塞了其中一个IIS服务线程。因此,寻找解决方法的方式如下:

IAsyncResult BeginProcessRequest(HttpContext context,AsyncCallback cb,Object extraData)
{
    this.del = new AsyncTaskDelegate(ProcessRequest);
    this.del.BeginInvoke(context, cb, extraData);  
}
void EndProcessRequest(IAsyncResult result)
{
    this.del.EndInvoke(ar);
}

这似乎会创建另一个线程来调用ProcessRequest()。因此,我们仍然在<Access DB>上停滞不前,但这次我们使用的是不属于IIS的线程。对我而言,这很好看,保持了代码的简洁和易读性。
与同事交流时,他说这样做没有任何好处,因为线程仍然被阻塞。我同意,但反驳说它不是IIS线程,所以对我有好处。但是他声称,如果我们使用BeginProcessRequest()来确保只有<Access DB>是异步完成的,那么我们可以获得更多好处,因为没有线程会被阻塞。
以下是伪代码,因为我还没有解决细节问题:
void ProcessRequest(HttpContext context)
{ /* Do Nothing */ }
IAsyncResult BeginProcessRequest(HttpContext context,AsyncCallback cb,Object extraData)
{
     <Init Stuff>
     this.command = <Access DB>.getSQLCommand();
     this.command.BeginExecuteNonQuery(cb,extraData);   // Assume we want to wait for this to complete.
}
void EndProcessRequest(IAsyncResult result)
{
     this.command.EndExecuteNonQuery(result);
     <Write Output to conext stream>
}

如果这不会阻塞执行BeginExecuteNonQuery()的线程,那么我认为这是一种优势。但这需要底层实现使用select()来检测数据库调用是否有数据等待读取。虽然更容易实现的方法是只需暂停等待响应的线程(在这种情况下,添加额外的细粒度并没有给我带来任何好处)。
因此,有人有参考资料表明哪种方法实际上更好吗?或者对BeginExecuteNonQuery()的底层实现有什么说明?或者只是一些可能有帮助的一般信息。
谢谢!
编辑:
SqlConnection类包含一个线程池。
因此,当您在SqlCommand上执行BeginExecuteNonQuery()时,它实际上不需要创建线程。请求被发送,当从SQL Server返回数据时,主控线程将从池中旋转一个线程。因此,上述第3个选项不会浪费线程或在异步执行DB操作时导致线程挂起。
1个回答

2
问题在于,从逻辑上讲,你仍然需要使线程停止。你的进程需要在ProcessRequest()方法中接受HttpContext,做一些事情(数据库访问),并将一些内容写回上下文。
HTTP的无状态性意味着套接字连接被打开,请求被发出,客户端等待响应,连接被关闭。你没有一个开放的连接只是等着你的处理程序向客户端回写一些东西。在你上面的例子中,EndProcessRequest()从哪里获取它的HttpContext对象来写输出?如果它是传递给BeginRequest的同一个对象,那么客户端将在请求和响应之间花费整个时间等待,同时保持一个开放的连接,直到服务器完成它的工作并发送一些输出。该上下文及其相关的连接必须保持打开状态,只要你的单独线程正在运行,这意味着你实际上正在阻塞ASP.NET线程。
除非你启动了多个线程并且可以保证它们会在合理的时间内完成,否则你不会得到任何好处。
此外,这是不必要的。为了使线程执行时间成为问题,你必须尝试“同时”处理更多请求,而服务器无法处理。如果你的“db access”方法花费的时间太长,你将排队你的请求,这最终会导致超时。这里的解决方案是尽可能缩短响应时间(页面呈现时间和所有呈现所需的数据库访问等)。
总结:
1.不要开启单个额外的线程——只有在运行多个线程时才有用
2.必须确保你启动的任何其他线程将在HTTP请求的分配时间内完成
3.调用ASP.NET线程必须等待直到响应被写回Http上下文,或者直到超时发生。如果你正在ProcessRequest方法中启动新线程,这可能会导致孤立线程。
简而言之,不要这样做。如果你需要从BeginRequest启动异步进程,并且不必向客户端写回输出,请考虑编写一个Windows服务来处理这些请求。将每个请求记录到表中,并使你的服务轮询该表以查看需要执行什么操作。通过AJAX,使你的客户端轮询该表以查看何时准备好结果。这可能不适合你的应用程序,但这是处理这种问题的一种方法。

5
调用的 ASP 线程不必等待,否则 IHttpAsyncHandler 就没什么意义了。从 BeginProcessRequest() 返回到调用 cb 的时间段内,调用线程可以愉快地处理其他请求,然后调用 EndProcessRequest() 进行清理。如果你只是在尝试减少与数据库相关站点的线程争用,那么 HTTP 超时可能是需要考虑的问题,但它们已经足够长了,在这种情况下,OP(或者他的同事)的方法非常完美。 - Simon Buchan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接