2016年11月14日更新,因为我看到一些答案(非常感谢)正在询问更多细节......
语言:VB.Net / Framework: 4.5
亲爱的TPL专家,我是任务处理库(TPL)的新手,非常感谢以下任何帮助:
我需要处理一个大文件(几十万条记录)。每条记录都是一个制表符分隔的记录,最多包含4个字段。
对于每个读取的记录,我需要提取字段并调用asnyc任务来处理这些字段。
显然,我需要通过一次只允许处理最多10
个记录来限制流程,也就是说,一次最多启动10
个任务。
例如,如果我读取了第一个10
条记录,那么将启动10
个任务。如果任何任务之一完成,我会读取下一条记录并启动另一项任务,依此类推,直到任务结束。基本上,我想优化我可以同时阅读和处理的记录数量。
到目前为止,这是我提出的:
rec_list = New StreamReaderEnumerable( file_spec )
For each rec In rec_list
task_list.Add( Task.Run( Async Function()
Return Await task_func( rec )
End Function
)
)
If ( task_list.Count >= 10 ) Then
Task.WhenAll( task_list )
task_list.Clear()
End If
Next
“StreamReaderEnumerable”类用于一次从文件中返回一条记录(作为Enumerable源),然后我将一个Task添加到一个列表中,以便一次处理10个任务。
没有必要维护任何类型的订单,因为每个任务可以随时完成 - 我只是想提高一次处理大于一个记录的效率。
问题是,我正在等待所有10个任务完成才能继续。最好进一步优化它。
我想我正在寻找的是ForEachAsync类型的枚举器 - 但还没有找到任何明确的例子......
任何建议都将受到赞赏。
答案 0 :(得分:1)
您应该查看BlockingCollection
class。您可以通过10
限制集合的大小,创建一个消费者任务,它将为集合添加一行,以及一些生产者任务,这些任务将从集合中占用一行直到文件末尾。像这样:
var numbers = new BlockingCollection<string>(10);
// this task should be refactored to accept a method
Task.Run(() =>
{
while (!lines.IsCompleted)
{
try
{
var line = lines.Take();
// do stuff here
}
catch (InvalidOperationException)
{
Console.WriteLine("Adding was completed!");
break;
}
}
Console.WriteLine("No more lines to take.");
});
// A simple blocking producer with no cancellation.
Task.Run(() =>
{
while (!END_OF_FILE_CHECK)
{
// this method will block until you have less than 10 lines in a collection
lines.Add(line);
}
lines.CompleteAdding();
});
这两个任务都应该使用LongRunning
flag创建,因为这将为所有这些任务提供一个线程:
指定任务将是一个长时间运行的粗粒度操作,涉及比细粒度系统更少,更大的组件。它提供了
TaskScheduler
提示可能需要超额认购的提示。 Oversubscription允许您创建比可用硬件线程数更多的线程。它还向任务调度程序提供了一个提示,即任务可能需要一个额外的线程,这样它就不会阻止本地线程池队列中其他线程或工作项的前进。
正如@ScottChamberlain建议的那样,其他方法是使用具有有限并行度的TPL Dataflow
的相同生产者/消费者模式,如下所示:
var workerBlock = new ActionBlock<string>(
line =>
{
// do stuff here
},
// No more than 10 lines simultaneosly
new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = 10
});
while (!END_OF_FILE_CHECK)
{
workerBlock.Post(line);
}
workerBlock.Complete();
// Wait for all messages to propagate through the network.
workerBlock.Completion.Wait();
您还可以引入一些生产者块like here in tutorial,并添加一些与worker
相关联的其他块来处理它的结果。