简单场景 - 我想阻止BlockingCollection<T>
(如果它是空的)。另一方面,我想阻塞一个ManualResetEvent对象,用于在需要终止线程时发出信号。
我了解无法使用WaitHandle.WaitAny
,因为BlockingCollection<T>
上的阻止会同时返回某个项目,并且不适合WaitHandle.WaitAny
指示的API。< / p>
我能想到的最简单的方法是使用超时阻塞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转移到使用取消令牌,如果能够提供更优雅的解决方案。
答案 0 :(得分:3)
最简单的选择是使用CancellationToken
(通过CancellationTokenSource
发出信号)而不是ManualResetEvent
(正如评论中建议的那样)与GetConsumingEnumerable
结合使用:
var blockingCollection = ...
foreach (var obj in blockingCollection.GetConsumingEnumerable(cancellationToken))
{
// process the obj
}
允许使用类似于ManualResetEvent
的构造并且在不浪费线程的情况下异步“阻塞”的另一个选项是使用TPL Dataflow
的{{1}}和{{{ 1}}:
BufferBlock
您可以根据Stephen Toub的Building Async Coordination Primitives, Part 1: AsyncManualResetEvent编写自己的AsyncManualResetEvent
或使用Visual Studio SDK中的AsyncManualResetEvent
答案 1 :(得分:0)
CancellationTokenSource就是您所需要的;将其插入控制台应用程序并调用它。
private static void DoSomeWork()
{
BlockingCollection<int> coll = new BlockingCollection<int>();
CancellationTokenSource source = new CancellationTokenSource();
ThreadPool.QueueUserWorkItem((s) =>
{
Console.WriteLine("Thread started. Waiting for item or cancellation.");
try
{
var x = coll.Take(source.Token);
Console.WriteLine("Take completed.");
}
catch (OperationCanceledException)
{
Console.WriteLine("Take cancelled. IsCancellationRequested={0}", source.IsCancellationRequested);
}
});
Console.WriteLine("Press ENTER to cancel wait.");
Console.ReadLine();
source.Cancel(false);
Console.WriteLine("Cancel sent. Press Enter to exit.");
Console.ReadLine();
}