从单个基于线程的任务中收集来自多个基于IO的任务的数据

时间:2013-01-22 03:10:34

标签: c# io task-parallel-library tpl-dataflow

使用TPL,如何从多个IO源("无线程"任务)收集结果,并将它们合并为一个序列,因为它们来自各自的源,而不会产生每个源的基于线程的任务监控他们?从一个线程中轮询源是否安全?

while (true)
{
    try
    {
        IEnumerable<UdpClient> readyChannels = 
            from channel in channels
            where channel.Available > 0
            select channel;

        foreach( UdpClient channel in readyChannels)
        {
           var result = await channel.ReceiveAsync();
           //do something with result like post to dataflow block.
        }
    }
    catch (Exception e)
    {
        throw (e);
    }
    ...

这样的事情怎么样?

1 个答案:

答案 0 :(得分:1)

我在这里看到几个选项:

如果你想激活对ReceiveAsync()的调用,将它们设置为对结果做一些事情(例如发送到数据流块,就像你说的那样)然后忘记它们,你可以使用{{1 }}:

ContinueWith()

这样做的一个缺点是你需要在每个延续中处理异常。

可能更好的方法是使用Stephen Cleary的AsyncEx中的OrderByCompletion()。这样,您可以立即开始所有读取并在完成时处理它们:

foreach (var channel in readyChannels)
{
   channel.ReceiveAsync().ContinueWith(task => 
   {
       var result = task.Result;
       //do something with result like post to dataflow block.
   }
}

另一个选项,例如,如果要限制并行性,则可以使用var tasks = readyChannels.Select(c => c.ReceiveAsync()).OrderByCompletion(); foreach (var task in tasks) { var result = await task; //do something with result like post to dataflow block. }

TransformBlock

如果您想将结果发送到另一个区块,那么上面评论中提到的处理只需将它们链接在一起:

var receiveBlock = new TransformBlock<UdpClient, UdpReceiveResult>(
    c => c.ReceiveAsync(),
    new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = degreeOfParallelism });
foreach (var channel in readyChannels)
    receiveBlock.Post(channel);
receiveBlock.Complete();

// set up processing here

await receiveBlock.Completion;

在上述所有情况中,永远不会有线程阻塞来监视任何事情。但是调用receiveBlock.LinkTo(anotherBlock); 然后处理结果的代码必须执行某处