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

问题描述 投票:0回答:2

过去,我使用 ThreadPool.QueueUserWorkItem 从管理器类中生成多个线程。该管理器类订阅这些生成的线程中的事件,该事件在线程工作完成时引发。然后,管理器类可以使用

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();

任何能指引我正确方向的想法都会很棒。我自己有一些,但我正在寻找最佳实践。我已阅读这篇文章,但它不包含任何对我有帮助的解决方案。

c# .net multithreading parallel-processing
2个回答
1
投票

使用 EnumerateDirectories (Fx 4) 而不是 GetDirectories。您当前的代码不能很好地并行工作。

其余的取决于您是否需要按顺序输出。
如果您不关心顺序,则可以简单地锁定输出流(使用辅助对象),写入并继续。不需要复杂的事件。
如果你想维持秩序,

将输出推送到队列。当 ForEach 完成时处理队列或启动一个单独的任务(消费者)以尽快写入。这将是典型的生产者/消费者模式。

请注意,通过并行处理,保持目录写入的顺序变得非常困难。


0
投票

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

也许让你的

Directory
类实现
IEnumerable<FileInfo>
并使用递归、
EnumerateDirectories
EnumerateFiles
懒惰地枚举所有文件。 (请参阅 http://msdn.microsoft.com/en-us/library/dd997370.aspx)。

现在您可以处理使用

IEnumerable
并处理它的问题,而无需混合代码来递归目录。

创建输出流。枚举

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

或者,也许更干净一些,启动一个单独的消费者

Task
来进行写入,并使用
BlockingCollection
在生产者和消费者之间传递数据(请参阅 http://msdn.microsoft.com/en-us /library/dd267312.aspx).

当您创建生产者任务时,您可能需要传入选项来限制最大并行度,因为当前任务调度程序在添加线程来完成工作时并不需要磁盘抖动。

另请参阅https://web.archive.org/web/20221208104708/http://reedcopsey.com/2010/03/17/parallelism-in-net-part-14-the- Different-forms-of-任务/ 以及 Reed 在 TPL 上的所有其他博客文章。

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

© www.soinside.com 2019 - 2024. All rights reserved.