我有一项服务可以确保同时显示一个弹出窗口。 AddPopupAsync
可以同时调用,即弹出窗口打开而另外10个AddPopupAsync
请求丢失。代码:
public async Task<int> AddPopupAsync(Message message)
{
//[...]
while (popupQueue.Count != 0)
{
await popupQueue.Peek();
}
return await Popup(interaction);
}
但是由于缺乏线程安全性,我可以发现可能发生的两件不必要的事情:
弹出方法适用于TaskCompletionSource
,在调用SetResult
方法之前,会调用popupQueue.Dequeue()
。
我考虑使用TryPeek
中的原子ConcurrentQueue
来使#1线程安全:
do
{
Task<int> result;
bool success = popupQueue.TryPeek(out result);
if (!success) break;
await result;
}
while (true);
然后我读到了AsyncProducerConsumerCollection,但我不确定这是否是最简单的解决方案。
如何以简单的方式确保线程安全?谢谢。
答案 0 :(得分:7)
要简单地添加线程安全性,您应该使用ConcurrentQueue
。它是队列的线程安全实现,您可以像使用队列一样使用它,而不必担心并发。
但是如果你想要一个队列,你可以await
异步(这意味着你没有忙等待或在等待时阻塞一个线程)你应该使用TPL Dataflow的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
不仅是线程安全的,它还为您提供异步协调(通常在消费者和生产者之间)。