awaitable Task based queue

前端 未结 9 1186
礼貌的吻别
礼貌的吻别 2020-11-27 14:44

I\'m wondering if there exists an implementation/wrapper for ConcurrentQueue, similar to BlockingCollection where taking from the collection does not block, but is instead a

9条回答
  •  小蘑菇
    小蘑菇 (楼主)
    2020-11-27 15:30

    Here's the implementation I'm currently using.

    public class MessageQueue
    {
        ConcurrentQueue queue = new ConcurrentQueue();
        ConcurrentQueue> waitingQueue = 
            new ConcurrentQueue>();
        object queueSyncLock = new object();
        public void Enqueue(T item)
        {
            queue.Enqueue(item);
            ProcessQueues();
        }
    
        public async Task DequeueAsync(CancellationToken ct)
        {
            TaskCompletionSource tcs = new TaskCompletionSource();
            ct.Register(() =>
            {
                lock (queueSyncLock)
                {
                    tcs.TrySetCanceled();
                }
            });
            waitingQueue.Enqueue(tcs);
            ProcessQueues();
            return tcs.Task.IsCompleted ? tcs.Task.Result : await tcs.Task;
        }
    
        private void ProcessQueues()
        {
            TaskCompletionSource tcs = null;
            T firstItem = default(T);
            lock (queueSyncLock)
            {
                while (true)
                {
                    if (waitingQueue.TryPeek(out tcs) && queue.TryPeek(out firstItem))
                    {
                        waitingQueue.TryDequeue(out tcs);
                        if (tcs.Task.IsCanceled)
                        {
                            continue;
                        }
                        queue.TryDequeue(out firstItem);
                    }
                    else
                    {
                        break;
                    }
                    tcs.SetResult(firstItem);
                }
            }
        }
    }
    

    It works good enough, but there's quite a lot of contention on queueSyncLock, as I am making quite a lot of use of the CancellationToken to cancel some of the waiting tasks. Of course, this leads to considerably less blocking I would see with a BlockingCollection but...

    I'm wondering if there is a smoother, lock free means of achieving the same end

提交回复
热议问题