在IHttpAsyncHandler中使用Task或async/await

Tom*_*uλa 15 asp.net ihttpasynchandler threadpool task-parallel-library async-await

自从我想要添加线程时编写ASP.NET应用程序以来,我可以通过3种简单的方法在ASP.NET应用程序中完成线程化:

  • 使用System.Threading.ThreadPool.
  • 使用自定义委托并调用其BeginInvoke方法.
  • System.Threading.Thread类的帮助下使用自定义线程.

前两种方法提供了一种快速方法来为您的应用程序启动工作线程.但不幸的是,它们损害了应用程序的整体性能,因为它们使用ASP.NET使用的相同池中的线程来处理HTTP请求.

然后我想使用新的Task或async/await来编写IHttpAsyncHandler.您可以找到的一个例子是Drew Marsh在这里解释的:https://stackoverflow.com/a/6389323/261950

我的猜测是使用Task或async/await仍然消耗来自ASP.NET线程池的线程,我不想要显而易见的原因.

可以告诉我,如果我可以在后台线程上使用Task(async/await),就像使用System.Threading.Thread而不是线程池一样吗?

在此先感谢您的帮助.

托马斯

Ste*_*ary 17

这种情况是Task,asyncawait真正大放异彩.这是相同的示例,重构以充分利用async(它还使用我的AsyncEx库中的一些辅助类来清理映射代码):

// First, a base class that takes care of the Task -> IAsyncResult mapping.
// In .NET 4.5, you would use HttpTaskAsyncHandler instead.
public abstract class HttpAsyncHandlerBase : IHttpAsyncHandler
{
    public abstract Task ProcessRequestAsync(HttpContext context);

    IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
    {
        var task = ProcessRequestAsync(context);
        return Nito.AsyncEx.AsyncFactory.ToBegin(task, cb, extraData);
    }

    void EndProcessRequest(IAsyncResult result)
    {
        Nito.AsyncEx.AsyncFactory.ToEnd(result);
    }

    void ProcessRequest(HttpContext context)
    {
        EndProcessRequest(BeginProcessRequest(context, null, null));
    }

    public virtual bool IsReusable
    {
        get { return true; }
    }
}

// Now, our (async) Task implementation
public class MyAsyncHandler : HttpAsyncHandlerBase
{
    public override async Task ProcessRequestAsync(HttpContext context)
    {
        using (var webClient = new WebClient())
        {
            var data = await webClient.DownloadDataTaskAsync("http://my resource");
            context.Response.ContentType = "text/xml";
            context.Response.OutputStream.Write(data, 0, data.Length);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

(如代码中所述,.NET 4.5具有HttpTaskAsyncHandlerHttpAsyncHandlerBase上面类似的内容).

很酷有关的事情async是,它并不需要任何一边做后台运行的线程:

  • ASP.NET请求线程启动请求,并使用它开始下载WebClient.
  • 虽然下载是怎么回事时,await返回了的async方法,使请求线程.该请求线程返回到线程池 - 留下0()个线程为此请求提供服务.
  • 下载完成后,该async方法将在请求线程上恢复.该请求线程仅用于编写实际响应.

这是最佳的线程解决方案(因为需要请求线程来编写响应).

原始示例也最佳地使用线程 - 就线程而言,它与async基于代码的线程相同.但IMO的async代码更容易阅读.

如果您想了解更多信息async,我的博客上有一个简介.

  • `async` - 本身 - 将*不*使用任何后台线程.除非你*通过调用`Task.Run`明确告诉它*. (4认同)
  • @RoyiNamir:后台线程仅在您明确请求后台线程时使用(例如,`Task.Run`).只有少数例外(例如,从"MemoryStream"异步读取),BCL将永远不会代表您这样做.所以*真的很容易*避免不必要的后台线程 - 只是不要使用它们! (2认同)
  • 不确定你得到了什么.使用`DownloadDataTaskAsync`的`WebClient`下载不需要后台线程. (2认同)

Tom*_*uλa 7

我一直在通过互联网寻找信息几天.让我总结一下我迄今发现的情况:

ASP.NET ThreadPool事实

  • 正如Andres所说:当async/await不会消耗额外的ThreadPool线程时?仅在您使用BCL异步方法的情况下.使用IOCP线程执行IO绑定操作.

  • 安德烈斯继续... 如果你想异步执行一些同步代码或你自己的库代码,这些代码可能会使用一个额外的线程池线程,除非你明确的使用IOCP线程池或您自己的线程池.

但据我所知,你不能选择whetever要使用IOCP线程,并作出正确的执行线程池是不值得的.我怀疑有人做了一个已经存在的更好的.

  • ASP.NET使用公共语言运行时(CLR)线程池中的线程来处理请求.只要线程池中有可用的线程,ASP.NET就可以轻松调度传入的请求.

  • 异步delegates使用ThreadPool中的线程.

什么时候开始考虑实现异步执行?

  • 当您的应用程序执行相对冗长的I/O操作(数据库查询,Web服务调用和其他I/O操作)时

  • 如果你想做I/O工作,那么你应该使用I/O线程(I/O完成端口),特别是你应该使用你正在使用的任何库类支持的异步回调.他们的名字以单词Begin和开头End.

  • 如果请求的计算成本低廉,那么并行性可能是一种不必要的开销.

  • 如果传入的请求速率高,则增加了更多并行可能会产生一些好处,实际上可能会降低性能,因为工作的传入速率可以足够高,以保持CPU的繁忙.

我应该创建新线程吗?

  • 避免创建新线程,就像避免瘟疫一样.

  • 如果您实际上排队了足够的工作项以防止ASP.NET处理进一步的请求,那么您应该将线程池挨饿!如果你是在同一时间运行字面上数百个CPU密集型操作的,它会做什么好事,有另一个工作者线程来服务ASP.NET请求,当机器已经超负荷.

和TPL?

  • TPL可以适应使用流程中的可用资源.如果服务器已经加载,则TPL可以使用少至一个工作程序并进行前进.如果服务器大部分是免费的,那么他们可以增长使用ThreadPool可以节省的工作量.

  • 任务使用线程池线程来执行.

参考