在我想要添加线程时编写ASP.NET应用程序的开始,我可以通过3种简单的方法在ASP.NET应用程序中完成线程化:
System.Threading.ThreadPool
。 BeginInvoke
方法。 System.Threading.Thread
class。前两种方法提供了一种快速方法来为您的应用程序启动工作线程。但不幸的是,它们损害了应用程序的整体性能,因为它们使用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
类而不是来自线程池?
提前感谢您的帮助。
托马斯
答案 0 :(得分:16)
这种情况是Task
,async
和await
真正闪耀的地方。这是相同的示例,重构为充分利用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);
}
}
}
(如代码中所述,.NET 4.5的HttpTaskAsyncHandler
与上面的HttpAsyncHandlerBase
类似。
关于async
的非常酷的事情是它在执行后台操作时不会使用任何线程:
WebClient
开始下载。await
实际从async
方法返回,留下请求线程。该请求线程返回给线程池 - 留下0(零)线程为此请求提供服务。async
方法。该请求线程仅用于编写实际响应。这是最佳的线程解决方案(因为需要请求线程来编写响应)。
原始示例也最佳地使用线程 - 就线程而言,它与基于async
的代码相同。但IMO async
代码更容易阅读。
如果您想了解有关async
的更多信息,我的博客上有intro post。
答案 1 :(得分:7)
我一直在网上寻找信息几天。让我总结一下我到现在所发现的东西:
ASP.NET ThreadPool事实
正如Andres所说:当async / await不会消耗额外的ThreadPool线程时?仅在您使用BCL异步方法的情况下。使用IOCP线程执行IO绑定操作。
Andres继续...... 如果您尝试异步执行某些同步代码或您自己的库代码,该代码可能使用额外的ThreadPool线程,除非您明确使用IOCP ThreadPool或您自己的ThreadPool。
但据我所知,你不能选择你想要使用IOCP线程,并且正确实现threadPool是不值得的。我怀疑有人做了一个已经存在的更好的。
ASP.NET使用公共语言运行时(CLR)线程池中的线程来处理请求。只要线程池中有可用的线程,ASP.NET就可以轻松调度传入的请求。
异步delegates
使用ThreadPool中的线程。
什么时候开始考虑实现异步执行?
当您的应用程序执行相对冗长的I / O操作(数据库查询,Web服务调用和其他I / O操作)时
如果你想进行I / O工作,那么你应该使用I / O线程(I / O完成端口),特别是你应该使用你所支持的任何库类所支持的异步回调使用。他们的名字以Begin
和End
开头。
如果请求的计算成本低廉,那么并行性可能是一种不必要的开销。
如果传入的请求率很高,那么添加更多的并行性可能会带来很少的好处,并且实际上可能会降低性能,因为传入的工作速率可能高到足以使CPU保持忙碌。
< / LI>我应该创建新主题吗?
避免创建新线程,就像避免瘟疫一样。
如果您实际上正在排队足够的工作项以防止ASP.NET处理进一步的请求,那么您应该将线程池挨饿!如果您同时运行数百个CPU密集型操作,那么当机器已经过载时,让另一个工作线程为ASP.NET请求提供服务会有什么好处。
和TPL?
TPL可以适应使用流程中的可用资源。如果服务器已经加载,则TPL可以使用少至一个工作程序并进行前进。如果服务器大部分是免费的,那么他们可以增加使用ThreadPool可以使用的工作人员数量。
任务使用线程池线程来执行。
<强>参考强>
答案 2 :(得分:3)
说“0(零)线程将为此请求提供服务”并不完全准确。 我认为你的意思是“来自ASP.NET ThreadPool”,并且在一般情况下是正确的。
当async / await不会消耗额外的ThreadPool线程时? 仅在您使用BCL异步方法(如WebClient异步扩展提供的方法)时才使用IOCP线程执行IO绑定操作。
如果您尝试异步执行某些同步代码或您自己的库代码,那么该代码可能会使用额外的ThreadPool线程,除非您明确使用IOCP ThreadPool或您自己的ThreadPool。
谢谢, 安德烈斯。
答案 3 :(得分:1)
Parallel Extensions团队在使用TPL和ASP.NET时a blog post解释了TPL和PLINQ如何使用ASP.NET ThreadPool。该帖子甚至还有一个决策图表,可以帮助您选择正确的方法。
简而言之,PLINQ在线程池中每个核心使用一个工作线程来执行查询,如果流量很大,可能会导致问题。
另一方面,Task和Parallel方法将适应进程的资源,并且可以使用一个线程进行处理。
就Async CTP而言,async / await构造与直接使用Tasks之间几乎没有概念上的区别。编译器使用一些魔法将等待转换为幕后的Tasks和Continuations。最大的区别是你的代码更清晰,更容易调试。
答案 4 :(得分:1)
要考虑的另一件事是async / await和TPL(Task)不是一回事。
请阅读这篇优秀的帖子http://blogs.msdn.com/b/ericlippert/archive/2010/11/04/asynchrony-in-c-5-0-part-four-it-s-not-magic.aspx,了解为什么async / await并不意味着“使用后台主题”。
回到我们的主题,在你想要在AsyncHandler中执行一些昂贵的计算的特定情况下,你有三个选择:
1)将代码保留在Asynchandler中,因此昂贵的计算将使用ThreadPool中的当前线程。 2)使用Task.Run或Delegate在另一个ThreadPool线程中运行昂贵的计算代码 3)从自定义线程池(或IOCP threadPool)中的另一个线程中运行昂贵的计算代码。
第二种情况可能对你来说足够了,这取决于你的“计算”过程运行多长时间以及你有多少负荷。安全选项是#3,但在编码/测试方面要贵得多。我还建议始终将.NET 4用于使用异步设计的生产系统,因为.NET 3.5中存在一些硬性限制。
答案 5 :(得分:0)
SignalR项目中的.NET 4.0有一个很好的HttpTaskAsyncHandler实现。您可能希望将其删除:http://bit.ly/Jfy2s9