我正在从一个接口公开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;
}
答案 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,太...