【发布时间】:2014-11-24 20:58:25
【问题描述】:
我有一项服务可以确保同时显示一个弹出窗口。 AddPopupAsync 可以同时调用,即一个弹出窗口打开,而另外 10 个 AddPopupAsync 请求进入。代码:
public async Task<int> AddPopupAsync(Message message)
{
//[...]
while (popupQueue.Count != 0)
{
await popupQueue.Peek();
}
return await Popup(interaction);
}
但我可以发现由于缺乏线程安全性而可能发生的两件事:
- 如果队列为空,Peek 会抛出异常
- 如果线程 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<T>有你需要的内置功能。 -
您当前的语义是所有对
AddPopupAsync的调用在所有弹出窗口都显示之前不会完成(不仅仅是那个特定调用显示的那个)。你确定这是你想要的语义吗? -
@Stephen Cleary 因为 PopupView 和 PopupViewModel 是单例,所以每当调用 Popup 时,模态弹出窗口的状态都会改变。这就是我使用队列的原因。
-
将这种类似于 UI 服务的东西视为 UI 的一部分难道不是很有意义吗?即,仅从 UI 上下文调用
AddPopupAsync? -
@Stephen Cleary:如果仅从 UI 线程调用 AddPopupAsync,由于 await 关键字,AddPopupAsync 在工作线程上运行。正确的?您是否想说不需要异步函数,因为 AddPopupAsync 中没有计算绑定的工作,如果不再有异步函数,代码将是线程安全的?
标签: c# .net thread-safety async-await