使用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);
}
...
这样的事情怎么样?
答案 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);
然后处理结果的代码必须执行某处。