繁体   English   中英

读写作为并行任务

[英]Reading and Writing as Parallel Tasks

寻找一种最佳方法来读取诸如Azure Table Storage之类的数据源,这是费时的,并将数据转换为json或csv并写入具有名称取决于分区键的本地文件。
正在考虑的一种方法是在计时器经过的事件触发器上以固定的时间间隔运行写入文件任务。

对于不能很好地并行化的事物(例如I / O),最好的做法是使用“生产者-消费者模型”。

它的工作方式是您有一个线程处理不可并行化的任务,该任务所做的所有工作都被读入缓冲区。 然后,您将拥有一组并行任务,这些任务均从缓冲区读取并处理数据,然后在完成数据处理后将数据放入另一个缓冲区。 如果您随后需要以不可并行的方式再次写出结果,则可以执行另一个任务来写出结果。

public Stream ProcessData(string filePath)
{
    using(var sourceCollection = new BlockingCollection<string>())
    using(var destinationCollection = new BlockingCollection<SomeClass>())
    {
        //Create a new background task to start reading in the file
        Task.Factory.StartNew(() => ReadInFile(filePath, sourceCollection), TaskCreationOptions.LongRunning);

        //Create a new background task to process the read in lines as they come in
        Task.Factory.StartNew(() => TransformToClass(sourceCollection, destinationCollection), TaskCreationOptions.LongRunning);

        //Process the newly created objects as they are created on the same thread that we originally called the function with
        return TrasformToStream(destinationCollection);
    }
}

private static void ReadInFile(string filePath, BlockingCollection<string> collection)
{
    foreach(var line in File.ReadLines(filePath))
    {
        collection.Add(line);
    }

    //This lets the consumer know that we will not be adding any more items to the collection.
    collection.CompleteAdding();
}

private static void TransformToClass(BlockingCollection<string> source, BlockingCollection<SomeClass> dest)
{
    //GetConsumingEnumerable() will take items out of the collection and block the thread if there are no items available and CompleteAdding() has not been called yet.
    Parallel.ForEeach(source.GetConsumingEnumerable(), 
                      (line) => dest.Add(SomeClass.ExpensiveTransform(line));

    dest.CompleteAdding();
}

private static Stream TrasformToStream(BlockingCollection<SomeClass> source)
{
    var stream = new MemoryStream();
    foreach(var record in source.GetConsumingEnumerable())
    {
        record.Seralize(stream);
    }
    return stream;
}

我强烈建议您阅读免费的《 并行编程模式》一书,其中有一些详细信息。 整节详细介绍了生产者-消费者模型。

更新 :对于小性能启动,请使用Parallel.ForEach循环中的Parallel.ForEach Extension Extras中的GetConsumingPartitioner()而不是GetConsumingEnumerable() ForEach对传入的IEnumerable做出了一些假设,即它会通过不需要分区的而不是枚举的方式来获取不需要的额外锁,从而不需要获取那些额外的锁。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM