尝试从.NET中的文件流中高效并行处理某些数据(读写)

时间:2017-06-08 07:16:08

标签: c# .net parallel-processing stream async-await

我试图了解如何有效地从文件中读取一些数据,做一些并行工作(每行)然后将新行写回文件系统。

我知道我可以做到这一点,一次一行..但我希望一次做几行 - 或者......如果一行是忙碌的'等待异步工作完成,然后转到下一行,等等。

这里有一些示例数据和逻辑...

Header
SomeId#1, SomeId#2, SomeId#3, Name, Has this line been processed and cleaned(true/false)

File Data
444,2,12,Leia Organa, true
121,33333,4,Han Solo, true
1,2,3,Jane Doe, false
1,4,11,John Doe, false

所以前两行已被处理,我将跳过这些行。 需要处理第3行和第4行。检查数据后,我希望将其保存回文件,如

1,33333,3,Jane Doe, true

所以这是一般逻辑......

  • 读取行
  • 致电DoWorkAsync()< - 可能需要一秒或5
  • 再次将此行保存回文件。

我只是希望我不必等待DoWorkAsync()完成才能保存然后阅读下一行。我希望我可以开始阅读下一行......如果前一行完成..罚款..然后将该行保存到文件中的相同行号..然后再次移动到下一行。

就像我可以在同一时间工作5或10行一样......等待结果从第三方api回来......并行工作或其他什么。

这可以在.NET中完成吗?我确信.NET具有这方面的功能......我无法看到这样做的模式。

注意:我通常执行异步/等待I / O密集型操作(如命中文件系统或调用某些第三方api端点)和我用于CPU密集工作的Parallel.ForEach。

注意:为什么行尾的true/false?因为我无法一次处理所有线路。我有api限制。

其他想法是有两个文件,一个用于PENDING,另一个用于PROCESSED。

1 个答案:

答案 0 :(得分:1)

这是一个并行处理器的存根,它在批量处理行时使用async / await

此方法可确保在编写时保留相同的顺序。

public async Task ProcessFile()
{
    const int parallelism = 5;

    using (var readStream = File.OpenRead(@"c:\myinputfile"))
    {
        // put HERE your logic for skipping to a specific line
        // e.g. readStream.Seek(lastPosition); 

        using (var reader = new StreamReader(readStream))
        {
            while (!reader.EndOfStream)
            {
                var tasks = new List<Task<string>>();

                for (var i = 0; i < parallelism; i++)
                {   
                    var line = await reader.ReadLineAsync();

                    tasks.Add(DoWorkAsync(line));

                    if (reader.EndOfStream)
                        break;
                }

                var results = await Task.WhenAll(tasks);

                using (var writeStream = File.Open(@"d:\myresultfile", FileMode.Append))
                using (var writer = new StreamWriter(writeStream))
                {
                    foreach (var line in results)
                        await writer.WriteLineAsync(line);
                }
            }
        }
    }
}

public async Task<string> DoWorkAsync(string line)
{
    await Task.Delay(new Random().Next(1000, 5000));
    // do some work and return line with last parameter = true
    return line.Replace("false", "true"); // e.g.
}

它肯定需要改进,但它应该为你自己写一个很好的基础。