使用Task库进行多线程处理

时间:2012-04-09 10:22:37

标签: .net asp.net-mvc multithreading threadpool task-parallel-library

ASP.NET MVC中的

我有一个Action接受用户输入的电子邮件地址列表,验证它们,然后将HTML和文本版本的电子邮件消息发送给每个收件人。然后将用户重定向到感谢页面。因为在发送所有电子邮件之前没有必要等待重定向到感谢页面,所以我使用Task.Factory.StartNew方法来启动实际发送电子邮件的新任务,并且它工作正常;用户立即被重定向到感谢页面,电子邮件消息在单独的线程上发送。 所以一切都按照我的意愿运作,但我仍然有关于多线程的以下问题。我上次阅读了很多帖子,但我仍然没有得到所有答案,这里有一些我从这些帖子中提取的事实(注意这些只是我的假设,我在这里写下来,所以你可以发表评论并改进它们:

  1. 任务库使用线程池中的线程

    如果您正在使用Task库,那么您将使用ASP.NET线程池中的线程创建新线程。这意味着少一个线程可用于为应用程序提供其他ASP.NET请求。因此,通过使用任务库,您不会通过将任务卸载到其他OS线程来优化ASP.NET线程的使用,只有用户体验更好,但任务库使用另一个可用于提供其他ASP.NET请求的线程。所以唯一的结果是用户不需要等待。

  2. 手动话题

    如果您确实想使用OS线程,则必须明确地启动新线程。但即使您启动新的OS线程,您也需要拥有多个内核或处理器的计算机才能真正看到应用程序可伸缩性的改进。

  3. 后台线程池

    有些帖子谈论ASP.NET线程池和单独的应用程序的后台线程池,它用于后台任务。换句话说,每个ASP.NET应用程序都有一个线程池来提供应用程序请求,另一个线程池来提供后台任务。我不认为这是真的,我认为每个ASP.NET应用程序只有一个线程池,并且来自此池的线程用于服务器:应用程序请求和后台任务。也许这可以说是Windows Forms应用程序,当通常只有一个线程运行(UI线程),你必须显式启动新线程。但ASP.NET的基础是多线程的。

  4. 以下是问题:

    1. 我已经阅读过Dino Esposito编程ASP.NET MVC 2中的异步MVC控制器。他写了关于异步控制器如何使用OS线程进行长时间运行的任务,因此最初为ASP.NET请求提供服务的ASP.NET线程现在可以自由地为其他请求提供服务。

      虽然我在这里不需要异步控制器,但我的问题是如何在我的示例中使用这样的OS线程。我是否必须明确地启动单独的线程,或者这也是可能的任务库?

    2. 即使我将这些应用程序卸载到某些操作系统线程,如果该机器只有一个处理器,也会有任何好处吗?我认为多核机器是真正提高应用程序可扩展性的必要条件。

2 个答案:

答案 0 :(得分:4)

您遇到的真正问题是ASP.NET可以随时回收您的AppDomain。发生这种情况时,您的后台线程将因极端偏见而中止。如果您使用线程池线程或启动自己的线程并不重要 - 如果ASP.NET不“了解”该线程,它可以并将销毁AppDomain并使用它获取线程。

“正确”的解决方案是拥有一个单独的Windows服务进程,即WCF服务器。然后,您的Web应用程序可以向服务发送命令,该服务执行它们 - 例如发送一些电子邮件。

快速而肮脏的方法是在您的网络应用中发送隐藏的动作以发送电子邮件。启动对该Action的异步请求,然后返回“thanks”页面而不等待结果。 ASP.NET并不关心请求是否来自您的应用程序 - 它只是将其视为另一个请求。因为ASP.NET“知道”了请求,所以它不会中止它。

答案 1 :(得分:1)

  

[...]他写了关于异步控制器如何将OS线程用于长时间运行的任务,因此最初为ASP.NET请求提供服务的ASP.NET线程现在可以自由地为其他请求提供服务。

那么,这取决于你在异步控制器中有什么代码。 可以在操作系统Thread上执行某些代码,但它不必执行。通常发生的是您启动一些在运行时不使用任何线程的异步操作。完成后,结果会在某个ThreadPool线程上执行。

  

虽然我在这里不需要异步控制器,但我的问题是如何在我的示例中使用这样的OS线程。我是否必须明确地启动单独的线程,或者这也可以使用任务库?

您可以使用TaskCreationOptions.LongRunning开始新主题。这不能保证启动一个新线程,但在实践中确实如此。

  

即使我将这些tak卸载到某个OS线程,如果该机器只有一个处理器,那么会有什么好处吗?我认为多核机器是真正提高应用程序可扩展性的必要条件。

如果该线程同步执行某些IO绑定操作,则会有好处。例如,发送电子邮件所花费的大部分时间很可能不是花费在使用CPU上,而是等待服务器响应。如果是这种情况,使用另一个线程(无论是单独的线程还是来自ThreadPool的线程)都是有益的。当然,如果异步执行该操作会更好,这不会阻塞任何线程,但这可能会使您的代码变得更加复杂(并且可能会出现甚至不可能的情况)。

关于你写的其他事情:

  

如果您正在使用Task库,那么您将使用ASP.NET线程池中的线程创建新线程。这意味着少一个线程可用于为应用程序提供其他ASP.NET请求

是的,在正常情况下(如果您使用默认TaskScheduler并且未指定TaskCreationOptions.LongRunning)。但是如果你遇到问题,更好的解决方案可能是增加ThreadPool限制,而不是自己为短期运行任务创建Thread。这是因为创建和销毁线程是一项昂贵的操作,如果性能对您很重要,则应该避免使用它。

  

[E] ach ASP.NET应用程序有一个线程池来提供应用程序请求,另一个线程池来提供后台任务。我不认为这是真的,我认为每个ASP.NET应用程序只有一个线程池,并且来自此池的线程用于服务器:应用程序请求和后台任务。

稍微复杂一点,但是,是的,ASP.NET请求和ThreadPool任务(使用ThreadPool.QueueUserWorkItem()启动或通过使用默认选项启动Task)在同一个线程池上运行