如何:BeginProcessRequest

时间:2010-01-26 15:47:53

标签: c# sql http asynchronous

编写在IIS上运行的服务。

基本上看起来像这样:

void ProcessRequest(HttpContext context)
{
     <Init Stuff>
     <Access DB>   // This may potentially stall for DB access
     <Write Output to conext stream>
}

通过在&lt; Access DB&gt;中停止线程我们基本上阻止了一个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()。因此,我们仍然停留在&lt; Access DB&gt;但这一次我们使用不属于IIS的线程停止运行。对我来说这很好,对称,保持代码清洁,易于阅读。

与同事交谈,他说这不会给你带来什么,因为线程仍然停滞不前。我同意但反驳说它不是一个IIS线程所以它确实给我买了东西。但他声称如果我们使用BeginProcessRequest()来确保只有&lt; Access DB&gt;完成异步,然后我们可以购买更多,因为没有线程会停滞。

这是psedu代码,因为我还没有详细说明:

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()来检测DB调用何时实际上有等待读取的数据。虽然更容易实现它的方法是停止线程等待响应(在这种情况下,添加额外的粒度不会给我任何东西)。

所有人都有任何参考指出实际上更好的方法是什么? 或者说明BeginExecuteNonQuery()的底层实现是如何工作的? 或者只是可能有用的任何一般信息。

由于

编辑:

SqlConnection类包含一个线程池。

因此,当您在SqlCommand上执行BeginExecuteNonQuery()时,它实际上不需要创建线程。发送请求,当从SQL-Server返回数据时,主控制线程将从池中启动一个线程。因此,在执行数据库操作异步时,上面的选项3不会浪费线程或导致线程挂起。

1 个答案:

答案 0 :(得分:2)

这里的问题是,从逻辑上讲,您仍然需要停止线程。您的进程需要在其ProcessRequest()方法中接受HttpContext,执行某些操作(Db访问),并将某些内容写回上下文。

HTTP的无状态特性意味着打开套接字连接,发出请求,客户端等待响应,连接关闭。您没有打开连接只是等待处理程序将某些内容写回客户端。在上面的示例中,EndProcessRequest()从哪里获取其HttpContext对象来编写其输出?如果它是传递给BeginRequest的那个,如果你试图让你的输出回到那个客户端,它必须是,那么客户端将花费整个时间在请求和响应之间等待 - 同时保持一个开放的连接 - 让服务器做它的事情并发送一些输出。只要您的单独线程正在旋转,该上下文及其相关连接必须保持打开状态,这意味着您实际上是在同一时间阻止ASP.NET线程。

除非您启动多个线程,否则您无法获得额外的线程,并且可以保证它们将在合理的时间内完成。

此外,这是不必要的。为了使线程执行时间成为问题,您可能会尝试“同时”处理比服务器可以处理的更多请求。如果您的“数据库访问”方法花费的时间太长,您将最终排队等待请求,这最终会导致超时。这里的解决方案是尽可能缩短您的响应时间(页面呈现时间和渲染所需的所有数据库访问等)。

要点:

  1. 不要触发一个额外的线程 - 这仅在运行多个线程时有用

  2. 您必须确保您启动的任何其他线程将在HTTP请求的分配时间内完成

  3. 调用ASP.NET线程必须等到将响应写回Http上下文,或者直到发生超时。如果您在ProcessRequest方法中启动新的线程,则可能会导致孤立线程。

  4. 简而言之,不要这样做。如果您需要从BeginRequest启动异步的进程并且不必将输出写回客户端,请考虑编写Windows服务来处理这些请求。将每个请求记录到一个表中,让您的服务轮询该表以查看需要执行的操作。让您的客户端轮询该表以查看结果何时准备好(可能通过AJAX)。这可能不适合您的应用程序,但是处理此类问题的一种方法。