我正在使用BlockingCollection
创建一个队列,其中多个线程可以添加项目,这些项目由单独的线程处理。添加的项目还包含一个回调函数(委托),在处理项目后调用该函数。要添加项目,请使用TryAdd
方法。
这一切都运行正常,但我知道在运行await
后是否有任何办法TryAdd
进行处理。
我想要的是,添加Thread等待处理完成然后继续。
我希望我能很好地描述我的问题,以便任何人都能给我一个提示。
我的目标框架是Mono / .Net4.5。
答案 0 :(得分:1)
唯一的解决方案是合作 - 处理方必须通知您物品已处理完毕。实现这一点非常简单 - 一种方法是:
public struct SignalizableItem<T>
{
private readonly T _value;
private readonly TaskCompletionSource<object> _signaller;
public SignalizableItem(T value, TaskCompletionSource<object> signaller)
{
_value = value;
_signaller = signaller;
}
public void Process(Action<T> action)
{
try
{
action(_value);
_signaller.SetResult(default(object));
}
catch (Exception ex)
{
_signaller.SetException(ex);
}
}
}
public static class BlockingCollectionExtensions
{
public static Task QueueAndWaitAsync<T>
(this BlockingCollection<SignalizableItem<T>> @this, T value)
{
var tcs = new TaskCompletionSource<object>();
@this.Add(new SignalizableItem<T>(value, tcs));
return tcs.Task;
}
}
用法非常简单 - 在制作人方面,你只需要
await collection.QueueAndWaitAsync(value);
在消费者方面,您将在准备好时打开值并发出信号:
var item = collection.Take();
item.Process
(
data =>
{
// Your processing
...
}
);
当然,该集合将是BlockingCollection<SignalizableItem<YourType>>
而不是BlockingCollection<YourType>
。
您可以通过添加其他扩展方法来进一步简化处理:
public static void Process<T>
(this BlockingCollection<SignalizableItem<T>> @this, Action<T> action)
{
@this.Take().Process(action);
}
实现取消(简单的CancellationToken
应该可以正常工作)或其他形式的关闭也可能是个好主意。
实际可用的东西最终会以
结束public static void ProcessAll<T>
(this BlockingCollection<SignalizableItem<T>> @this, Action<T> action,
CancellationToken cancellationToken)
{
SignalizableItem<T> val;
while (@this.TryTake(out val, -1, cancellationToken)) val.Process(action);
}
抽象出整个处理机制,只暴露简单的动作委托。