繁体   English   中英

如何使用队列线程安全的异步方法

[英]How to make asynchronous methods that use a queue thread safe

我有一个服务,确保同时显示一个弹出窗口。 AddPopupAsync可以同时调用,即弹出窗口打开而另外10个AddPopupAsync请求AddPopupAsync 。代码:

public async Task<int> AddPopupAsync(Message message)
{
    //[...]

    while (popupQueue.Count != 0)
    {
        await popupQueue.Peek();
    }

    return await Popup(interaction);
}

但是我可以发现由于缺乏线程安全而可能发生的两件不必要的事情:

  1. 如果队列为空,Peek将抛出异常
  2. 如果线程A在Popup中的第一个语句之前被抢占,则另一个线程B将不会等待挂起的弹出A,因为队列仍然是空的。

Popup方法与TaskCompletionSource一起使用,在调用其SetResult方法之前,调用popupQueue.Dequeue()

我想考虑使用ConcurrentQueue中的原子TryPeek来使#1线程安全:

do
{
    Task<int> result;

    bool success = popupQueue.TryPeek(out result);

    if (!success) break;
    await result;
} 
while (true);

然后我读到了AsyncProducerConsumerCollection,但我不确定这是否是最简单的解决方案。

如何以简单的方式确保线程安全? 谢谢。

要简单地添加线程安全性,您应该使用ConcurrentQueue 它是队列的线程安全实现,您可以像使用队列一样使用它而不必担心并发。

但是,如果你想有一个队列,你可以await异步(这意味着你不是忙等待或阻塞的线程在等待),你应该使用TPL数据流的BufferBlock这是非常相似的AsyncProducerConsumerCollection通过MS你只已经实现了:

var buffer = new BufferBlock<int>();
await buffer.SendAsync(3); // Instead of enqueue.
int item = await buffer.ReceiveAsync(); // instead of dequeue.

ConcurrentQueue对于线程安全性很好,但在高级别的争用中是浪费的。 BufferBlock不仅是线程安全的,它还为您提供异步协调(通常在消费者和生产者之间)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM