【问题标题】:Should .Net 4.0 Tasks always be the preferred method for multi-threaded applications?.Net 4.0 任务是否应该始终是多线程应用程序的首选方法?
【发布时间】:2012-01-03 10:00:42
【问题描述】:

我正在阅读有关Task Parallel Library 的文章,文章说:

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

但它也说他们在幕后使用线程池。我难以弄清楚的是,如果仅在使用 ThreadPool 时才应使用任务(因此“线程与任务”将等同于“线程与线程池”),或者微软是否打算使用任务任何需要多个线程的地方,而无需考虑“线程与线程池”困境所固有的考虑。

那么,任务是否应该在需要多线程的地方使用?

【问题讨论】:

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


    【解决方案1】:

    使用任务的设计优势在于,您可以将线程的本质交给运行时,这大概可以使用错误更少、更优化的解决方案来完成线程任务。我知道某些基于任务的范例,例如 PLINQ,允许您提示运行时应该采用哪种策略,以便直接处理“到线程池还是不到线程池”的问题。

    切换到此模型类似于切换到托管 GC 语言与需要您清理自己的内存的语言。总会有支持后者的论据,但垃圾收集现在变得如此优化,以至于它实际上不是问题。理想情况下,Tasks 的运行时切换机制会发展并变得更好。因此,理论上,为 .NET 4 编写和编译的应用程序可以通过更好地实现运行时变得更快,而无需进一步重新编译。此外,众所周知,线程代码很难正确处理,因此任何隐藏这些细节的机制对程序员都有好处。

    这些好处是否大于潜在的危害,例如运行时不能很好地处理的边缘情况,应该逐案考虑。不过,我当然会尽量不要在这里提前优化。

    【讨论】:

    • 我的理解是长时间运行的任务最适合线程,而较短的任务更适合线程池。还有前景与背景的问题。 Task 是否真的确定传入的函数是否长时间运行,或者它应该是前台还是后台?我不明白它怎么可能。那么它怎么会问一个 ThreadPool 是否是最好的路由呢?如果它总是使用 ThreadPool,当 ThreadPool 不合适时,是否应该继续使用线程?
    • 你说得对,它不知道任务是否长时间运行,这就是停机问题。据我了解,运行时可以确定您的查询或函数是高度计算密集型还是 IO 密集型,并相应地选择策略。
    • 看不到任务长度与线程池问题有何关系。线程池的目的是通过对任意任务重用线程分配来最小化创建和终止线程的开销。但是线程池是使用线程实现的,由仲裁员管理。我不确定将长任务交给池是否真的有害,除了它如何处理线程管理。回到 Q,运行时可能会根据它确定的最佳策略选择更复杂的方法。即您可以使用 PostMessage 在一个线程上完成异步。
    • ps 真的很讨厌 cmets 的响应限制。
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      Task 是比线程或 ThreadPool 更高级别的抽象。本质上,您将一个函数打包到一个任务中,并要求运行时尽可能地执行它。您可以拥有数十个甚至数百个将由有限数量的线程执行的任务。

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

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

      您必须记住,虽然典型的笔记本电脑有 2 个内核,而典型的台式机有 4 个内核,但小型服务器已经有 8 个或更多内核,并且很快就会有更多内核。通过手动调度线程和避免阻塞来保持所有这些核心忙碌可能会成为一个大问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-10-03
        • 2017-02-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-10-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多