多线程或任务并行库

7

我有一个应用程序,使用多线程同时执行30个独立的任务,每个任务通过http检索数据,执行计算并将结果返回给ui线程。

我可以使用TPL执行相同的任务吗?

TPL是创建30个新线程并将它们分布到所有可用核心上,还是只是将任务分割到可用核心上并使用一个线程每个核心?

在这种情况下,使用TPL会提高性能吗,而不是使用多线程?

4个回答

9
作为一般规则,TPL没有限制使用比核心更多(或更少)的线程。
要在一定程度上控制使用TPL,我的第一步是:确保线程池最大线程设置至少为30,然后将任务并行化,最大并发级别为30。在任务中,在开始CPU绑定计算之前,可以使用信号量来限制并发性到核心数。如果您没有在IIS或SQL服务器下运行,则可以设置线程池线程的最小/最大数量为30,以防止线程池启发式太多地处理线程数。(当然,假设在您的应用程序此时没有其他目的使用TPL和线程池。)
最佳线程数量取决于情况。 例如,考虑您的场景:当任务检索数据时,它们不受CPU限制-它们受网络限制。随着任务的开始,增加并行性是明智的,以便同时进行下载。但是,您的计算可能受到CPU限制。在这种情况下,减少线程数,使每个核心只运行一个线程,可能会产生更好的性能。
TPL现在基于新的CLR线程池
线程池使用启发式算法来决定线程数量。
有一个Channel9视频介绍了新线程池的一些见解。
旧线程池的启发式算法和一些关于新线程池的内容可以在这里找到(最后一段“What the Future Holds?”)
算法和数字在不同版本的CLR中可能会发生变化。
将来也可能是这种情况。

有很多关于并发级别的帖子,我碰到过一个在这里


考虑到这些任务受网络限制,创建并启动30个新线程是否比让TPL处理线程更有效? - Bruce Adams
您也可以这样做-启动30个下载任务,然后使用信号量确保计算仅在您的CPU核心数量上运行。请注意,如果您从同一客户端向同一IP地址启动30个下载任务,则不符合HTTP规范的建议。 :) - Andras Vass
@Bruce:在你的情况下,线程可能会表现得更好。不过,我怀疑差别会很小。 - Andras Vass
感谢您提供关于TPL的信息和帮助,让我更好地理解它。谢谢。 - Bruce Adams

8
我相信TPL通常会使用每个核心一个线程,除非您明确告诉它使用更多。它可能会检测到这不足够的情况 - 例如,在您的情况下,您的任务将花费大部分时间等待数据。

有没有什么原因不能使用异步Web获取?我怀疑在这里没有必要为每个任务或甚至每个内核使用一个线程。 TPL使异步编程的各个方面变得更加容易,例如连续性。

就效率而言,您的应用程序实际上是CPU限制吗?听起来,您需要在网络端获得最大适当级别的并行性 - 这是要关注的部分,除非计算真的很重。


更新 - 非原作者提供

上面的答案一如既往地很好,但可能会误导,因为它没有 .NET 4.0 CLR 中的一些重要更改。

正如 Andras 所说,当前的 TPL 实现使用线程池,因此将使用所需的线程数(核心数量现在不再相关):

任务并行库(TPL)是一组新类,专门设计用于在现代硬件上更轻松、更高效地执行非常细粒度的并行工作负载。TPL 已经作为 CTP 单独提供了一段时间,并包含在 Visual Studio 2010 CTP 中,但在这些版本中,它 建立在自己的 专用工作调度程序 上的。对于 CLR 4.0 Beta 1,TPL 的默认调度程序将是 CLR 线程池,这允许 TPL 样式的工作负载与现有的基于 QUWI 的代码“友好相处”,并允许我们重用线程池中的大部分底层技术 - 特别是线程注入算法,我们将在未来的文章中讨论。

来源:

链接


@Jon:我相信这在TPL的CTP版本中是正确的。现在我认为情况已经不同了。 - Andras Vass
@andras:你能详细说明一下你的意思吗? - Jon Skeet
@Jon:我相信在TPL的CTP版本中(你可以在VS2008中使用),单线程/CPU情况是真实的。现在它使用新的CLR线程池-根据一些启发式算法,可能会创建更多的线程,也可能不会创建。 - Andras Vass
@Jon:确实可以为任务指定并行度。但是,您可以启动多个任务,最终得到比核心更多的线程。我也不确定当前或未来的TPL /线程池实现是否会在检测到CPU利用率不足时增加线程数。 - Andras Vass
@Bruce:看看像WebClient.DownloadStringAsync这样的东西。你不需要多个线程来同时进行多个请求。 - Jon Skeet
显示剩余7条评论

2
我有一个应用程序,使用多线程同时执行30个独立的任务,每个任务都通过http检索数据,进行计算并将结果返回给UI线程。这是一个IO密集型的并发程序。
可以使用TPL执行相同的任务,但TPL设计用于CPU密集型并行程序,因此您会滥用它。
TPL既不会创建30个新线程并在所有可用核心上分配它们,也不会仅将任务拆分到可用核心上并使用一个线程来处理每个核心。实际上,TPL使用每个核心的等待自由工作窃取任务队列,在运行时动态平衡CPU密集型计算。
在这种情况下,使用TPL比多线程可以节省30个线程的创建和额外争用造成的开销。
您问题的正确解决方法是编写一个异步程序,不会阻塞线程。这可以通过将下载完成后的计算剩余部分表示为连续体,并在下载完成时使用数据调用该连续体来完成。微软的新F#编程语言包括专门设计的功能,使此变得容易。例如,使用F#仅需要5行代码即可解决您的问题:
let fetchCalcAndPost uris calc post =
  for uri in uris do
    async { use client = new System.Net.WebClient()
            let! data = client.AsyncDownloadString uri
            do calc data |> post }
    |> Async.Start

这个解决方案不会阻塞任何线程,因此是完全并发的。


0

你是否生成了30个线程?你是否使用了线程池?我相信TPL会更加优化。生成线程是一项相当昂贵的操作。我同意Jon的观点,TPL通常会使用每个核心一个线程。顺便问一下,我们在谈论哪个.NET版本。


我不使用线程池。 如果任务是网络绑定的,TPL如何比多线程更优化? 比较的是在四核机器上同时执行30个任务和4个任务。 - Bruce Adams
如果任务是网络绑定的,那么我的答案就不适用了。抱歉,我误解了。 - Hannes de Jager

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