应该使用线程池或任务并行库的IO绑定操作

问题描述:

在我的项目还有点儿聚合器中的一个,我解析饲料,播客等从网络。应该使用线程池或任务并行库的IO绑定操作

如果使用顺序的方法,因为有大量的资源,它需要相当的时间来处理所有的人(因为网络问题以及类似的东西);

foreach(feed in feeds) 
{ 
    read_from_web(feed) 
    parse(feed) 
} 

所以我想要实现并发,不能决定我是否应该基本上使用线程池与工作线程处理或仅仅依靠第三方物流得到它排序。

ThreadPools肯定会为我处理工作线程的工作,我会得到我所期望的(并且在多核CPU环境中,其他核心也将被使用)。

concurrency

但我还是要考虑TPL太多,因为它是推荐的方法,但我有点担心它。首先我知道TPL使用ThreadPools,但增加了额外的决策层。我主要关心的是单核环境存在的情况。如果我没有错,TPL从一开始就有一个数字的工作线程开始,等于可用CPU内核的数量。我对TPL产生类似结果的担心是因为我的IO绑定案例的顺序方法。

因此,对于IO绑定操作(在我的情况下从web读取资源),最好是使用ThreadPools并控制事物,或者更好地仅依靠TPL? TPL也可以用于IO界限的情况吗?

更新:我主要关心的是 - 在单核CPU环境将TPL只是表现得像顺序方法还是会仍然提供并发?我已经在阅读Parallel Programming with Microsoft .NET,所以book,但无法找到确切的答案。

注意:这是对我以前的问题[Is it possible to use thread-concurrency and parallelism together?]的重新措辞,这是错误的措词。

所以我决定为此编写测试并在实际数据中看到它。

测试传奇

  • ITR:迭代
  • SEQ:顺序方法。
  • PrlEx:并行扩展 - Parallel.ForEach
  • TPL:任务并行库
  • TPOOL:线程池

测试结果

单核CPU [Win7-32​​] - - 在VMWare下运行 -

Test Environment: 1 physical cpus, 1 cores, 1 logical cpus. 
Will be parsing a total of 10 feeds. 
________________________________________________________________________________ 

Itr. Seq. PrlEx TPL  TPool 
________________________________________________________________________________ 

#1  10.82s 04.05s 02.69s 02.60s 
#2  07.48s 03.18s 03.17s 02.91s 
#3  07.66s 03.21s 01.90s 01.68s 
#4  07.43s 01.65s 01.70s 01.76s 
#5  07.81s 02.20s 01.75s 01.71s 
#6  07.67s 03.25s 01.97s 01.63s 
#7  08.14s 01.77s 01.72s 02.66s 
#8  08.04s 03.01s 02.03s 01.75s 
#9  08.80s 01.71s 01.67s 01.75s 
#10  10.19s 02.23s 01.62s 01.74s 
________________________________________________________________________________ 

Avg. 08.40s 02.63s 02.02s 02.02s 
________________________________________________________________________________ 

单核CPU [WinXP的] - VMWare的下运行 -

Test Environment: 1 physical cpus, NotSupported cores, NotSupported logical cpus. 
Will be parsing a total of 10 feeds. 
________________________________________________________________________________ 

Itr. Seq. PrlEx TPL  TPool 
________________________________________________________________________________ 

#1  10.79s 04.05s 02.75s 02.13s 
#2  07.53s 02.84s 02.08s 02.07s 
#3  07.79s 03.74s 02.04s 02.07s 
#4  08.28s 02.88s 02.73s 03.43s 
#5  07.55s 02.59s 03.99s 03.19s 
#6  07.50s 02.90s 02.83s 02.29s 
#7  07.80s 04.32s 02.78s 02.67s 
#8  07.65s 03.10s 02.07s 02.53s 
#9  10.70s 02.61s 02.04s 02.10s 
#10  08.98s 02.88s 02.09s 02.16s 
________________________________________________________________________________ 

Avg. 08.46s 03.19s 02.54s 02.46s 
________________________________________________________________________________ 

双核CPU [Win7-64]

Test Environment: 1 physical cpus, 2 cores, 2 logical cpus. 
Will be parsing a total of 10 feeds. 
________________________________________________________________________________ 

Itr. Seq. PrlEx TPL  TPool 
________________________________________________________________________________ 

#1  07.09s 02.28s 02.64s 01.79s 
#2  06.04s 02.53s 01.96s 01.94s 
#3  05.84s 02.18s 02.08s 02.34s 
#4  06.00s 01.43s 01.69s 01.43s 
#5  05.74s 01.61s 01.36s 01.49s 
#6  05.92s 01.59s 01.73s 01.50s 
#7  06.09s 01.44s 02.14s 02.37s 
#8  06.37s 01.34s 01.46s 01.36s 
#9  06.57s 01.30s 01.58s 01.67s 
#10  06.06s 01.95s 02.88s 01.62s 
________________________________________________________________________________ 

Avg. 06.17s 01.76s 01.95s 01.75s 
________________________________________________________________________________ 

四核CPU [Win7的-64】 - HyprerThreading支持 -

Test Environment: 1 physical cpus, 4 cores, 8 logical cpus. 
Will be parsing a total of 10 feeds. 
________________________________________________________________________________ 

Itr. Seq. PrlEx TPL  TPool 
________________________________________________________________________________ 

#1  10.56s 02.03s 01.71s 01.69s 
#2  07.42s 01.63s 01.71s 01.69s 
#3  11.66s 01.69s 01.73s 01.61s 
#4  07.52s 01.77s 01.63s 01.65s 
#5  07.69s 02.32s 01.67s 01.62s 
#6  07.31s 01.64s 01.53s 02.17s 
#7  07.44s 02.56s 02.35s 02.31s 
#8  08.36s 01.93s 01.73s 01.66s 
#9  07.92s 02.15s 01.72s 01.65s 
#10  07.60s 02.14s 01.68s 01.68s 
________________________________________________________________________________ 

Avg. 08.35s 01.99s 01.75s 01.77s 
________________________________________________________________________________ 

总结

  • 无论你在单核环境或多核一个,并行扩展运行,TPL和线程池相同的行为,并给出近似的结果
  • 不过TPL具有优势一样容易异常处理,消除支持,并轻松地返回任务结果能力。尽管并行扩展也是另一种可行的选择。在

运行测试自己

您可以下载源here和你自己的运行。如果你可以发布结果,我也会添加它们。

更新:修复了源代码链接。

+6

+1使用科学方法解决您的问题。如果我没有弄错,所有上述技术都使用线程池,这解释了类似的结果。一般来说,我喜欢使用并行扩展,因为语法非常简单,而且我很懒。 – 2012-04-16 00:14:14

+0

我已经进一步清理了源代码; https://github.com/raistlinthewiz/concurrency-tests – HuseyinUslu 2013-10-11 09:56:20

+1

感谢您完成所有的测试工作。 – GregoryBrad 2014-12-31 07:06:39

我害怕TPL对序列方法产生类似的结果来处理IO界限的情况。

我认为会。什么是瓶颈?解析还是下载?从网上下载多线程对你无能为力。

我会用任务并行库进行裁剪,将面罩或影响了下载的图像,cuting从播客等,这是更具扩展性的一些示例。

但它不会在数量级上加速。花你的资源来实现一些功能,测试。

PS。 “哇我的功能在0.7秒而不是0.9”;)

+0

瓶颈是下载内容。一个普通用户订阅了10个以上的订阅源,而从我的测试中,它需要15+秒以上 - 至少对于我的测试子集 - 下载并用顺序方法解析所有这些订阅源。我觉得有足够的改进空间。 – HuseyinUslu 2011-03-06 22:24:25

+0

根据我发布的测试结果,似乎确实有足够的改进空间。 – HuseyinUslu 2011-03-07 12:07:51

您可以将自己的task scheduler分配给TPL任务。默认的work stealing虽然很聪明。

+0

我读过这些资源,其实我很担心,在.NET Framework 4中,默认任务调度程序与线程池紧密集成。如果使用默认任务调度程序,则执行并行任务的工作线程由.NET ThreadPool类管理。 **通常,在您的计算机上至少有与核心**数量相同的工作线程。 - 如果我在单核环境中运行,该怎么办?我找不到任何澄清。 – HuseyinUslu 2011-03-06 22:32:34

+1

因此,您希望强制执行最小_平行度,例如,最后X度,在这种情况下? 'TaskCreationOptions.LongRunning' [might](http://social.msdn.microsoft.com/Forums/en-US/parallelextensions/thread/8304b44f-0480-488c-93a4-ec419327183b/)就是你想要的。自定义调度程序是另一种选择,如果你不能说服默认的人做你想做的事。 – ehnmark 2011-03-06 22:53:18

+0

其实它似乎基于我发布的测试结果,我的担忧无效。 – HuseyinUslu 2011-03-07 12:07:23

你是正确的TPL确实,当你创建你自己的线程池去除一些你有控制的。但是如果你不想深入挖掘,这只是正确的。 TPL允许您创建长期运行的任务,这些任务不属于TPL线程池,可以很好地满足您的需求。已出版的免费阅读的书籍Parallel Programming with Microsoft .NET将使您更深入地了解TPL是如何使用的。 您始终可以选择Para​​lle.For,任务explicit 参数应该分配多少个线程。除此之外,如果您想完全控制,您可以用您自己的替换TPL调度程序。

如果你想最大限度地提高吞吐量IO密集型任务,你绝对必须结合了传统的异步处理模型(APM)的API与TPL基础的工作。当异步IO回调处于挂起状态时,APM API是解锁CPU线程的唯一方式。 TPL提供the TaskFactory::FromAsync helper method以协助组合APM和TPL代码。

查看MSDN上名为TPL and Traditional .NET Asynchronous Programming的.NET SDK的这一部分,了解如何结合这两种编程模型实现异步涅。的更多信息。

如果您将调用并行化到网址,我认为它会改善您的应用程序,即使只有一个核心。 拿这个代码来看看:

var client = new HttpClient(); 
var urls = new[]{"a", "url", "to", "find"}; 

// due to the EAP pattern, this will run in parallel. 
var tasks = urls.Select(c=> client.GetAsync(c)); 

var result = Tasks.WhenAll(task).ContinueWith(a=> AnalyzeThisWords(a.Result)); 
result.Wait(); // don't know if this is needed or it's correct to call wait 

在这种情况下,多线程和异步的区别是回调/完成是怎么做的。

使用EAP时,任务数量与线程数量无关。

由于您依赖于GetAsync任务,http客户端使用网络流(socket,tcp客户端或其他)并在BeginRead/EndRead完成时发出信号通知它以引发事件。所以,这一刻没有线程参与。

完成调用后,可能会创建一个新线程,但是它取决于TaskScheduler(用于调用GetAsync/ContinueWith调用)来创建新线程,使用现有线程或内联任务以使用调用线程。

如果AnalyzeThisWords阻塞了太多时间,那么随着ContinueWith上的“回调”由线程池工作者完成,您就开始遇到瓶颈。