等待添加到BlockingCollection

时间:2015-09-01 14:14:26

标签: c# multithreading

我正在使用BlockingCollection创建一个队列,其中多个线程可以添加项目,这些项目由单独的线程处理。添加的项目还包含一个回调函数(委托),在处理项目后调用该函数。要添加项目,请使用TryAdd方法。

这一切都运行正常,但我知道在运行await后是否有任何办法TryAdd进行处理。 我想要的是,添加Thread等待处理完成然后继续。

我希望我能很好地描述我的问题,以便任何人都能给我一个提示。

我的目标框架是Mono / .Net4.5。

1 个答案:

答案 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);
}

抽象出整个处理机制,只暴露简单的动作委托。