我可以将while(true)循环转换为EventWaitHandle吗?

时间:2014-11-21 04:24:26

标签: c# multithreading parallel.foreach file-processing event-wait-handle

我尝试通过Parallel.ForEach将处理后的数据添加到BlockingCollection来处理大量文本文件。

问题是我希望Task taskWriteMergedFile使用集合并将它们至少每800000行写入结果文件。

我想我无法在迭代中测试集合大小,因为它是并行的,所以我创建了Task

在这种情况下,我可以将任务中的while(true)循环转换为EventWaitHandle吗?

const int MAX_SIZE = 1000000;
static BlockingCollection<string> mergeData;
mergeData = new BlockingCollection<string>(new ConcurrentBag<string>(), MAX_SIZE);


string[] FilePaths = Directory.GetFiles("somepath");

var taskWriteMergedFile = new Task(() =>
{
    while ( true )
    {
        if ( mergeData.Count  > 800000)
        {
            String.Join(System.Environment.NewLine, mergeData.GetConsumingEnumerable());
            //Write to file
        }
        Thread.Sleep(10000); 
    }
}, TaskCreationOptions.LongRunning);

taskWriteMergedFile.Start();
Parallel.ForEach(FilePaths, FilePath => AddToDataPool(FilePath));
mergeData.CompleteAdding();

1 个答案:

答案 0 :(得分:1)

你可能不希望这样做。相反,让您的任务将每行写入收到的文件。如果要将文件大小限制为80,000行,则在写入第80,000行后,关闭当前文件并打开一个新文件。

来想一想,你所拥有的东西是行不通的,因为GetConsumingEnumerable()在收集被标记为完成添加之前不会停止。会发生的事情是,在队列中有80,000个项目之前,事物将通过睡眠循环,然后它会在String.Join上阻塞,直到主线程调用CompleteAdding。有了足够的数据,你就会耗尽内存。

另外,除非你有充分的理由,否则你不应该在这里使用ConcurrentBag。只需使用BlockingCollection的默认值ConcurrentQueue即可。 ConcurrentBag是一个非常特殊的用途数据结构,其效果不如ConcurrentQueue

所以你的任务变成了:

var taskWriteMergedFile = new Task(() =>
{
    int recordCount = 0;
    foreach (var line in mergeData.GetConsumingEnumerable())
    {
        outputFile.WriteLine(line);
        ++recordCount;
        if (recordCount == 80,000)
        {
            // If you want to do something after 80,000 lines, do it here
            // and then reset the record count
            recordCount = 0;
        }
    }
}, TaskCreationOptions.LongRunning);

当然,假设您已在其他位置打开输出文件。最好在任务开始时打开输出,并在foreach退出后关闭它。

另一方面,您可能不希望生产者循环并行。你有:

Parallel.ForEach(FilePaths, FilePath => AddToDataPool(FilePath));

我不确定AddToDataPool正在做什么,但如果它正在读取文件并将数据写入集合,则会遇到一些问题。首先,磁盘驱动器一次只能做一件事,所以它最终会读取一个文件的一部分,然后是另一个文件的一部分,然后是另一个文件的一部分,等等。为了读取下一个文件的每个块,它必须寻求正确的位置。寻求磁盘头非常昂贵 - 5毫秒或更长。 CPU时间的永恒。除非您正在进行比读取文件花费更长时间的重载处理,否则您最好一次处理一个文件。除非您可以保证输入文件位于不同的物理磁盘上。 。

第二个潜在的问题是,在运行多个线程的情况下,您无法保证将事物写入集合的顺序。当然,这可能不是问题,但是如果您希望单个文件中的所有数据在输出中组合在一起,那么多个线程每个都会在集合中写入多行,就不会发生这种情况。

要记住一些事情。