3

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

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

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

  2. 手册线程

    如果你真的想使用操作系统线程,你必须明确地开始新的主题。但即使您启动新的操作系统线程,您也需要拥有多个内核或处理器的机器才能真正看到应用程序可扩展性的提高。

  3. 后台线程池

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

这里有几个问题:

  1. 我已阅读有关恐龙埃斯波西托在编程ASP.NET MVC 2异步MVC控制器。他写的异步控制器如何使用OS线程长期运行的任务,因此ASP.NET线程担任ASP.NET请求最初是现在可以自由地服务器的其他请求。

    虽然我并不需要异步控制器在这里,我的问题是我怎么能在我的例子中使用这样的OS线程。我是否必须明确启动单独的线程,或者这对于任务库来说也是可能的?

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

回答

1

[...]他写了关于异步控制器如何使用操作系统线程进行长时间运行的任务,因此最初服务于ASP.NET请求的ASP.NET线程现在可以免费服务于其他请求。

嗯,这取决于你有在异步控制器什么代码。它可能在OS Thread上执行一些代码,但它不必。通常会发生的是,您启动了一些异步操作,在运行时不使用任何线程。并且当它结束时,结果在一些ThreadPool线程上执行。

尽管我不需要异步控制器,但我的问题是如何在我的示例中使用这种操作系统线程。我是否必须明确启动单独的线程,或者这对于任务库来说也是可能的?

您可以使用​​开始一个新线程。这不保证开始一个新的线程,但实际上它确实。

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

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

至于其他的事情:

如果您正在使用任务库,您通过使用线程ASP.NET线程池创建新的线程。这意味着,一个线程较少的可用于服务于其他ASP.NET请求的应用

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

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

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

+0

svick,谢谢。总而言之,任务默认使用线程池中的线程。如果指定TaskCreationOptions.LongRunning,则可能会使用新的非线程池线程。 对于短期运行任务,最好增加线程池中的线程数量,而不是创建非线程池线程。 非线程池线程只能用于长时间运行的任务,这些任务可能会通过使用线程池线程伤害性能。 – Joxi 2012-04-10 07:40:53

4

你真正的问题是,ASP.NET可以随时回收你的AppDomain。当发生这种情况时,你的后台线程将被极端的偏见中止。不管你使用线程池线程还是启动你自己的线程 - 如果ASP.NET不能“知道”线程,它可以并且会销毁AppDomain,并把它带入你的线程。

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

快速和肮脏的方法是在您的web应用程序发送电子邮件隐藏的行动。启动对该Action的异步请求,然后返回“谢谢”页面而不等待结果。 ASP.NET不关心请求来自你的应用程序 - 它只是将它视为另一个请求。由于ASP.NET“知道”请求,它不会中止它。