.NET Parallel.ForEach,StreamWriter输出+线程安全

时间:2010-09-12 19:20:02

标签: c# .net multithreading parallel-processing

过去我使用ThreadPool.QueueUserWorkItem从manager类生成多个线程。此管理器类订阅这些生成的线程中的事件,该事件在线程工作完成时引发。然后,管理器类可以使用lock处理将输出写入文本文件以防止任何竞争条件。

现在我正在使用Parallel.ForEach来完成这项工作。以线程安全的方式将所有输出写入文本文件的最佳方法是什么?

我的实施基本概要:

public class Directory
{
    public string Path;

    public Directory(string path)
    {
        Path = path;
    }

    public void Scan()
    {
        Parallel.ForEach(new DirectoryInfo(Path).GetDirectories(),
                         delegate(DirectoryInfo di)
                         {
                             var d = new Directory(di.FullName);
                             d.Scan();
                             //Output to text file.
                         });

    }
}

我顺便过去了:

new Directory(@"c:\blah").Scan();

任何指出我正确方向的想法都会很棒。我有一些自己,但我正在寻找最佳实践。我已阅读this post但它不包含任何有助于我的解决方案。

2 个答案:

答案 0 :(得分:1)

使用 EnumerateDirectories (Fx 4)代替GetDirectories。您当前的代码不会同时并行工作。

<击> 其余部分取决于您是否需要输出有序 如果您不关心顺序,可以直接锁定输出流(使用辅助对象),写入并继续。无需复杂的事件。
如果您想维持秩序,

将输出推送到队列。 ForEach完成时处理队列或启动单独的Task(Consumer)以尽快编写它。这将是典型的生产者/消费者模式。

请注意,通过使处理并行,很难维持目录的编写顺序。

答案 1 :(得分:0)

对于初学者,我将枚举文件的概念与处理它们的概念分开。

也许让您的Directory类实现IEnumerable<FileInfo>并使用递归EnumerateDirectoriesEnumerateFiles懒惰地枚举所有文件。 (见http://msdn.microsoft.com/en-us/library/dd997370.aspx)。

现在,您可以处理使用IEnumerable并处理它而不需要混合代码来递归目录的问题。

创建输出流。枚举IEnumerable<FileInfo>并为每个Task启动:http://msdn.microsoft.com/en-us/library/dd321424.aspx。在该任务中,在读取文件并创建输出字符串后,锁定()并写入输出流。

或者,也许更清洁一点,启动一个单独的消费者Task进行写作并使用BlockingCollection在生产者和消费者之间传递数据(参见http://msdn.microsoft.com/en-us/library/dd267312.aspx)。 / p>

当您创建生成器任务时,您可能希望传入选项以限制最大并行度,因为磁盘抖动不是当前任务调度程序在添加线程以完成工作时所寻找的。

另请参阅http://reedcopsey.com/2010/03/17/parallelism-in-net-part-14-the-different-forms-of-task/以及所有Reed在TPL上的其他博客条目。

另请参阅链接TPL和RX的工作,例如: http://blogs.msdn.com/b/pfxteam/archive/2010/04/04/9990349.aspx这将为在这种情况下生成和消费提供更清晰的语法。