简单的场景 – 我想阻止BlockingCollection< T> (如果它是空的).另一方面,我想阻塞一个ManualResetEvent对象,用于在需要终止线程时发出信号.
我知道没有办法使用WaitHandle.WaitAny,因为阻塞BlockingCollection< T>同时返回一个项目,并且不适合WaitHandle.WaitAny规定的API.
我能想到的最简单的方法是阻止BlockingCollection< T>使用超时然后在等待句柄上等待0超时,如下所示:
ManualResetEvent term = ...;
BlockingCollection<T> coll = ...;
while (true)
{
T obj;
bool found = coll.TryTake(out obj, 500);
if (term.WaitOne(0))
{
break;
}
if (found)
{
// process the obj
}
}
但我觉得应该有一个更优雅的解决方案,可能使用其他东西而不是BlockingCollection< T>?
欢迎任何想法.
编辑1
我可以从MRE转到使用取消令牌,如果需要更好的解决方案.
最佳答案 最简单的选择是使用CancellationToken(通过CancellationTokenSource发出信号)而不是结合GetConsumingEnumerable的ManualResetEvent(在评论中建议):
var blockingCollection = ...
foreach (var obj in blockingCollection.GetConsumingEnumerable(cancellationToken))
{
// process the obj
}
允许使用类似于ManualResetEvent的构造并且在不浪费线程的情况下异步“阻塞”的另一个选项是使用TPL Dataflow的BufferBlock和AsyncManualResetEvent:
AsyncManualResetEvent term = ...
BufferBlock<T> buffer = ...
var termTask = term.WaitAsync();
while (true)
{
var receiveTask = buffer.ReceiveAsync();
if (termTask == await Task.WhenAny(receiveTask, termTask))
{
break;
}
T obj = await receiveTask;
// process the obj
}
您可以根据Stephen Toub的编写自己的AsyncManualResetEvent:Building Async Coordination Primitives, Part 1: AsyncManualResetEvent或使用Visual Studio SDK中的一个:AsyncManualResetEvent