.NET 4.0任务是否总是多线程应用程序的首选方法?

6
我正在阅读关于 Task Parallel Library 的文章,该文章说道:

在 .NET Framework 4 中,任务是编写多线程、异步和并行代码的首选 API。

但是文章还说他们在后台使用了 ThreadPool。我很难理解的是,如果只有在需要使用 ThreadPool 时才应该使用 Tasks (因此 "Thread versus Task" 将等同于 "Thread versus ThreadPool"),还是 Microsoft 所期望的是 Tasks 在任何需要多个线程的地方都可以使用,而不考虑 "Thread versus ThreadPool" 困境中的困扰。
所以,Tasks 是否应该在任何需要多个线程的地方使用?
3个回答

4
使用任务的设计优势在于将线程的细节交给运行时处理,这样可以使用更少bug、更优化的解决方案来完成线程任务。我知道某些基于任务的范例,例如PLINQ,允许你提示运行时应采用哪种策略,从而直接处理“使用线程池还是不使用线程池”的问题。
与切换到需要自己清理内存的语言相比,切换到托管 GC 的语言模式类似。后者总会有支持它的争议,但垃圾回收现在已经被优化得几乎不是问题了。理论上,Tasks 的运行时切换机制将会不断演进和提升效率。因此,在 .NET 4 下编写并编译的应用程序在更好的运行时实现下会变得更快,而无需重新编译。另外,线程代码是难以正确执行的,因此任何隐藏这些详细信息的机制都对程序员有好处。
是否因潜在的缺陷(如运行时无法处理的边界情况)而导致这些优点不足,应该根据具体情况进行考虑。我肯定会避免过早地进行优化。

据我了解,长时间运行的任务最适合使用线程,而较短的任务则更适合使用线程池。还有前台与后台的问题。任务是否真正能够确定传入的函数是长时间运行的,或者它应该是前台还是后台?我不知道它如何做到。那么它怎么能询问线程池是否是最佳路线呢?如果它总是使用线程池,那么在线程池不适用时,是否应继续使用线程? - Bob
你说得对,它无法知道任务是否长时间运行,这就是停机问题。据我所知,运行时可以确定查询或函数是否需要大量计算或IO操作,并相应地选择策略。 - cunningdave
不明白任务长度与Threadpool问题有什么关系。 Threadpool的目的是通过重复使用已分配的线程来最小化创建和销毁线程的开销。 但是,Threadpool使用线程来实现,由仲裁者管理。 我不确定将长时间任务交给池是否真的有害,除非考虑到它如何处理线程管理。回到问题,运行时可能会根据确定最佳策略的因素选择更复杂的方法。 例如,您可以通过使用PostMessage在一个线程上完成异步操作。 - cunningdave
非常讨厌评论的响应限制。 - cunningdave

1

您可以使用TaskCreationOptions.LongRunning作为提示,告诉TPL您的任务可能比ThreadPool调整的更复杂。但是,是的,TPL似乎是未来多线程编程的首选方法。微软甚至正在其上构建以支持Async CTP中提出的新的asyncawait关键字。这并不意味着您必须完全放弃旧的ThreadThreadPool API。然而,我个人发现TPL在更优雅的API中完成了我想要的大部分功能,现在几乎完全依赖它。


0

任务是比线程或线程池更高级的抽象。实质上,您将一个函数打包到一个任务中,并要求运行时尽其所能地执行它。您可以有许多几十个甚至数百个任务,这些任务将由有限数量的线程执行。

使用任务,开发人员可以创建所需数量的任务,将它们链接起来创建流程(在 F# 中称为工作流),并控制它们的取消,而不必担心如何分配或使用线程。运行时会选择最佳方式使用有限数量的线程执行所有任务。

任务使并发编程模式的实现变得更加容易。ParallelExtensionExtras 库提供了将 Begin/EndXXX 对转换为可链接的任务的方法,以及 Async CTP 中使用的任务迭代习惯用法的初步版本,该习惯用法提供了 async/await 语法。您可以使用任务的 ConcurrentCollection 创建作业队列,类似于 ParallelExtensionExtras 中的 AsyncCall 示例,或者进一步创建类似于 Scala、Erlang 或 F# 的代理。Async CTP 中的 DataFlow 是使用任务创建的另一个示例。

你必须记住,尽管典型的笔记本电脑有2个核心,典型的台式机有4个核心,但小型服务器已经拥有8个或更多的核心,很快它们将拥有更多。手动调度线程并避免阻塞以保持所有这些核心繁忙可能会成为一个重大问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接