在C#中的循环中使用ThreadPool

时间:2011-03-15 13:19:50

标签: c# multithreading threadpool

我不太熟悉线程,但下面的代码是可以接受的(我更担心在循环中使用线程池):

      string[] filePaths = GetFilePaths();

      foreach (string filePath in filePaths )
      {
        ThreadPool.QueueUserWorkItem(DoStuff, filePath); 
      }

还有其他方法可以做到这一点吗?

编辑:

N.B。每次执行DoStuff都会创建多个子线程(大约200个)。这些子线程模拟系统的用户,仅负责通过TCP接收和发送信息。

4 个答案:

答案 0 :(得分:3)

当以下两个组合成立时,您当前的代码可能会出错:

  • 文件很多
  • 处理文件(DoStuff)需要很长时间

Threadpool没有足够的负载平衡功能,并且会继续创建越来越多的线程,超出最佳数量。

如果您可以使用Fx4,请使用TPL。

对于早期版本,请重写代码以使用更少的线程。


编辑,因为您使用的是Fx4:

您最大的收获可能是使用System.Directory.EnumFiles()替换Directory.GetFiles()

草图:

var files = System.Directory.EnumerateFiles(...);  // deferred execution

Parallel.ForEach(files, f => DoStuff(f));  // maybe use MaxDegree or CancelationToken

// all files done here

您还可以将此.ForEach包装在(单个)try / catch等中

如果DoStuff()需要并行性,你也应该使用TPL,也可以传递CancellationToken等。它会将所有Parallelism置于单个调度程序的控制之下。

你可能需要协助进行微调,但这比没有TPL要容易得多。

答案 1 :(得分:2)

你担心是对的。 这种当前情况,取决于循环中有多少项,可能会对线程池施加太大的压力。我认为我在这里不正确,在这种情况下,项目将排队,但可能导致大部分线程池被利用,这不一定会带来最佳性能。

从.NET 4开始,使用线程池有一些非常简单的替代方法。

Parallel.ForEach在这里与您相关。以下是.NET 4中新的并行功能的链接:

http://galratner.com/blogs/net/archive/2010/04/24/a-quick-lap-around-net-4-0-s-parallel-features.aspx

更新:截至编辑时提及200个子线程。我会建议OS线程不是轻量级对象。它们具有与它们相关的开销,并且可以快速抵消并行性的任何收益。并行执行某项任务需要考虑一下工作量,目标是什么(释放UI,利用所有内核等),以及最终的工作是CPU密集型还是IO约束。还有其他因素,但我认为这些非常重要。我会创建另一个SO问题,通过使用这种级别的并行性来描述您试图解决的问题,这样您就可以获得一些更具体针对您的问题的设计建议。

答案 2 :(得分:2)

这取决于您要完成的任务:如果您希望在另一个线程上执行操作但不介意按严格顺序执行操作,则只需执行

string[] filePaths = GetFilePaths();
ThreadPool.QueueUserWorkItem(DoStuff, filePaths);

并将foreach放在DoStuff内。这可能是一个可接受的解决方案,具体取决于您期望filePaths具有的值(例如,如果所有路径都在同一设备上,尝试一次性完成所有路径都不会更快;甚至可能更慢),以及它绝对是最简单的。

如果你肯定想要并行执行它们,那么你应该查看Task Parallel Library(仅限.NET 4),特别是Parallel.ForEach。由于限制最大并发任务的数量是一个好主意,这里有一个示例,说明如何执行此操作:

var options = new ParallelOptions { MaxDegreeOfParallelism = 2 };
Parallel.ForEach(filePaths, options, i=> {
    DoStuff(i);
});

答案 3 :(得分:0)

为什么不将调用封装在Action<T>delegate中,并将它们放在循环中的线程安全队列中。然后你可以启动一个(多个)线程,这个线程一直有效,直到队列中的所有动作都被执行完毕;这样你可以控制使用的线程数量,你不必担心会产生过多的线程。