我正在寻找类似ConcurrentQueue的对象,如果队列为空,它将允许我等待Dequeue操作,因此我可以执行以下操作:
public static async Task ServiceLoop() {
var awaitedQueue = new AwaitedQueue<int>();
while (!cancelled) {
var item = await awaitableQueue.Dequeue();
Console.WriteLine(item);
}
}
我已经编写了以下课程,但是如果在调用出队(Dequeue)到新的等待者入队之间将一个项目添加到队列中,将会发生可怕的事情。
public class AwaitedQueue<T> : IDisposable {
ConcurrentQueue<TaskCompletionSource<T>> awaiters = new ConcurrentQueue<TaskCompletionSource<T>>();
ConcurrentQueue<T> items = new ConcurrentQueue<T>();
public AwaitedQueue() { }
public void Enqueue(T item) {
if (!awaiters.TryDequeue(out TaskCompletionSource<T> awaiter)) {
this.items.Enqueue(item);
} else {
awaiter.SetResult(item);
}
}
public async Task<T> Dequeue() {
if (items.TryDequeue(out T item)) {
return item;
} else {
// If an item is enqueued between this call to create a new TaskCompletionSource.
var awaiter = new TaskCompletionSource<T>();
// And this call to actually enqueue, I believe it will cause me problems.
awaiters.Enqueue(awaiter);
return await awaiter.Task;
}
}
public void Dispose() {
while (awaiters.TryDequeue(out TaskCompletionSource<T> awaiter)) {
awaiter.SetCanceled();
awaiter.Task.Wait();
}
}
}
我肯定已经有一个健壮且经过测试的实现,但是我不知道我需要在Google中键入哪种英语单词组合才能找到它。
答案 0 :(得分:0)
对此有一个现代的解决方案:Channels。 “通道”是异步的生产者/消费者队列。
频道也具有“完成”的概念,因此您可以完成频道而不用带有cancelled
标志。
用法:
public static async Task ServiceLoop() {
var awaitedQueue = Channel.CreateUnbounded<int>();
var queueReader = awaitedQueue.Reader;
while (await queueReader.WaitToReadAsync())
{
while (queueReader.TryRead(out var item))
{
Console.WriteLine(item);
}
}
}