在现有的IProducerConsumerCollection(T)上使用BlockingCollection(T)

时间:2013-09-10 16:55:05

标签: c#

我正在从一个接口公开IProducerConsumerCollection(T),该接口将定期为另一个线程添加项目以供使用。

public interface IProducer<T>
{
    IProducerConsumerCollection<T> ProducerCollection { get; }
}

我尝试在现有集合中使用BlockingCollection(T),但似乎不支持直接添加IProducerConsumerCollection(T)

var queue = new ConcurrentQueue<string>();
var blockingCollection = new BlockingCollection<string>(queue);

var task1 = Task.Run(() => {
    Console.WriteLine("Dequeued " + blockingCollection.Take());
    Console.WriteLine("Dequeued " + blockingCollection.Take());
});

var task2 = Task.Run(() => {
    Console.WriteLine("Enqueueing Hello");
    queue.Enqueue("Hello");

    Console.WriteLine("Enqueueing World");
    queue.Enqueue("World");
});

Task.WaitAll(task1, task2);

这将无限期挂起,因为BlockingCollection(T)不会注意到新项目。

是否有与BlockingCollection(T).Take方法类似的功能,或者有什么比:

更简单
static async Task<T> TakeAsync<T>(
    IProducerConsumerCollection<T> collection,
    CancellationToken token
)
{
    T result;

    while(!collection.TryTake(out result))
    {
        await Task.Yield();
        token.ThrowIfCancellationRequested();
    }

    return result;
}

1 个答案:

答案 0 :(得分:5)

  

这将无限期挂起,因为BlockingCollection(T)不会注意到新项目。

确实 - 因为您直接添加到queue。来自文档:

  

请勿直接修改基础集合。使用BlockingCollection<T>方法添加或删除元素。如果直接更改基础集合,BlockingCollection<T>对象可能会损坏。

因此,您对queue.Enqueue的来电应改为使用blockingCollection

TaskAsync而言 - 您可以使用TryTake,超时为Infinite( - 1)而不是......但您可能需要阅读Stephen Cleary's blog post on a similar topic,太...