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

C# .NET Parallel.ForEach,StreamWriter输出+;线程安全,c#,.net,multithreading,parallel-processing,C#,.net,Multithreading,Parallel Processing,在过去,我使用ThreadPool.QueueUserWorkItem从一个管理器类生成多个线程。此管理器类订阅这些衍生线程中的事件,该事件在线程工作完成时引发。然后,manager类可以通过使用锁来防止任何竞争条件,从而将输出写入文本文件 现在我使用Parallel.ForEach来完成这项工作。以线程安全的方式将所有输出写入文本文件的最佳方法是什么 我的实施的基本纲要: public class Directory { public string Path; public

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

现在我使用
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();

任何能给我指出正确方向的想法都会很好。我自己也有一些,但我正在寻找最佳实践。我读过,但它没有包含任何帮助我的解决方案。

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

对于其余部分,这取决于您是否需要输出有序。
如果不关心顺序,只需锁定输出流(使用helper对象)、写入并继续即可。不需要复杂的事件。
如果你想维持秩序

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


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

首先,我要将枚举文件的概念与处理文件的概念分开

也许可以让您的
目录
类实现
IEnumerable
,并使用递归、
枚举目录
枚举文件
惰性地枚举所有文件。(见附件)

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

创建输出流。枚举
IEnumerable
并为每个任务启动
任务:请参阅。在该任务中,读取文件并创建输出字符串后,锁定()并写入输出流

或者,也许更干净一些,启动一个单独的消费者
任务
,执行写入操作,并使用
阻止收集
在生产者和消费者之间传递数据(请参阅)

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

另请参阅Reed和所有其他关于TPL的博客文章


另请参阅链接TPL和RX的努力,例如,这将为这种情况下的生产和消费提供更清晰的语法。

您在向文件中写入什么?您能在查询中表达它吗?输出是使用Newtonsoft.JSON库序列化为JSON字符串的对象。
//输出到文本文件
中有什么内容?如果它触发一个事件,直接调用某个实际处理发生的
lock()
,那么并行执行这项操作将不会有多大效果。那么,就员工和经理之间的交流而言,当前事件是什么样的呢?@Hightechrider这是我需要帮助的一篇文章——它还没有写好。如果你想要一个更好的答案,那么至少发布一个非线程安全代码的概要。他为什么需要一个队列?难道他就不能按顺序把所有东西都恢复到原来的顺序吗?@GAbe:我不知道怎么做。处理DIR所用的时间会有所不同。EnumDirs已经是有序的(而不是并行的)。Henk:我在想象这样的事情:
outfile.Write(EnumDirs().AsParallel().Select(f=>Scan(f)).AsOrdered())
,以便
Scan
处理是并行的,但输出是按枚举顺序的。@Gabe:试试看,我想看看一个工作示例。但是AFAIK Asordered()不会在Select()之后工作亨克:你是对的;我放错地方了<代码>目录.EnumerateFileSystems(@“C:\”).AsParallel().AsOrdered().Select(x=>{Thread.Sleep(10);返回x;})
返回与
目录.EnumerateFileSystems(@“C:\”)相同的结果。选择(x=>{Thread.Sleep(10);返回x;})
速度更快;它的运行速度也和Directory.EnumerateFileSystemEntries(@“C:\”).AsParallel()一样快。选择(x=>{Thread.Sleep(10);返回x;})
,它以任意顺序返回结果。