首页 文章

使用Task库进行多线程处理

提问于
浏览
3

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

  • Task library uses thread from thread pool

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

  • Manual thread

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

  • Background thread pool

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

以下是问题:

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

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

  • 即使我将这样的设备卸载到某个OS线程,如果机器只有一个处理器,那么会有什么好处吗?我认为多核机器是真正提高应用程序可扩展性所必需的 .

2 回答

  • 4

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

    那么,这取决于你在异步控制器中有什么代码 . 它可以在OS Thread 上执行一些代码,但它在运行时不会使用任何线程 . 当它完成时,结果会在某个 ThreadPool 线程上执行 .

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

    您可以使用 TaskCreationOptions.LongRunning 启动新线程 . 这不能保证启动一个新线程,但实际上它确实如此 .

    即使我将这样的设备卸载到某个OS线程,如果机器只有一个处理器,会有什么好处吗?我认为多核机器是真正提高应用程序可扩展性所必需的 .

    如果该线程同步执行某些IO绑定操作,则会有好处 . 例如,发送电子邮件所花费的大部分时间很可能不是花费在使用CPU上,而是等待服务器响应 . 如果是这种情况,使用另一个线程(无论是单独的线程还是来自 ThreadPool 的线程)都是有益的 . 当然,如果异步执行该操作会更好,这甚至不可能获得't block any thread, but that may make your code much more complicated (and there may cases where it' .

    关于你写的其他事情:

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

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

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

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

  • 1

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

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

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

相关问题