TryTake与GetConsumingEnumerable的区别

4
解决方案1和解决方案2有什么区别?_taskQ是BlockingCollection,并尝试实现生产者-消费者场景。 BlockingCollection在内部使用默认的ConcurrentQueue进行存储。
//Solution 1
foreach (Action action in _taskQ.GetConsumingEnumerable())
{
    action(); // Perform task.
    Thread.Sleep(1000);
}

如果没有任何项目,TryTake 将会阻塞

//Solution 2
Action t;
while(_taskQ.TryTake(out t))
{
    t();
    Thread.Sleep(1000);
}

为什么不使用 System.Threading.Task 并像这样做:Task.Run(async delegate { await Task.Delay(1000); }); - MethodMan
@MethodMan 为什么你要在后台延迟?只需要使用 await Task.Delay(1000); 即可。 - piedar
5
你的假设是不正确的。迭代器会等待集合中出现项目。但当集合为空时,TryTake()返回false。调用Sleep()从来都不是避免烧掉100%核心的好方法,你会更喜欢使用迭代器。删除Sleep()调用即可。 - Hans Passant
@MethodMan,你的意思是用Task.Run替换Thread.Sleep(1000)吗?Task.Run在线程池上运行委托,因此不像Thread.Sleep(1000)那样阻塞。 - Helic
1个回答

11
bool TryTake(out T) 若没有任何项,则立即返回false。
bool TryTake(out T, TimeSpan) 若在超时时间内没有任何项,则返回false。 由GetConsumingEnumerable()返回的可枚举对象会阻塞,直到生产者调用CompleteAdding()并有项目可用。
在解决方案1中,您等待下一个动作并执行它; 这是一种合适的模式! 但是Thread.Sleep() 实际上并不是必要的,因为如果没有任何项可用,迭代将会阻塞。
在解决方案2中,您在有任何项的情况下获取并执行操作,然后如果消费者超过生产者,退出循环。这可能不是您想要的结果。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接