C#中是否有像ConcurrentQueue这样的数据结构,它允许我等待一个空队列,直到添加一个项目?

时间:2019-05-15 06:26:33

标签: c# asynchronous concurrency queue

我正在寻找类似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中键入哪种英语单词组合才能找到它。

1 个答案:

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