有时我会遇到访问对象字段的异步/等待代码。例如这个 snippet来自 Stateless 项目的代码:
private readonly Queue<QueuedTrigger> _eventQueue = new Queue<QueuedTrigger>();
private bool _firing;
async Task InternalFireQueuedAsync(TTrigger trigger, params object[] args)
{
if (_firing)
{
_eventQueue.Enqueue(new QueuedTrigger { Trigger = trigger, Args = args });
return;
}
try
{
_firing = true;
await InternalFireOneAsync(trigger, args).ConfigureAwait(false);
while (_eventQueue.Count != 0)
{
var queuedEvent = _eventQueue.Dequeue();
await InternalFireOneAsync(queuedEvent.Trigger, queuedEvent.Args).ConfigureAwait(false);
}
}
finally
{
_firing = false;
}
}
如果我理解正确,await **.ConfigureAwait(false)
表示在此 await
之后执行的代码执行 not necessarily必须在相同的上下文中执行。所以这里的 while
循环可以在 ThreadPool 线程上执行。我看不出是什么确保 _firing
和 _eventQueue
字段是同步的,例如,是什么在这里创建了锁/内存栅栏/屏障?所以我的问题是;我需要使字段线程安全,还是异步/等待结构中的某些东西负责这个?
编辑:澄清我的问题;在这种情况下,应始终在同一线程上调用 InternalFireQueuedAsync
。在那种情况下,只有延续可以在不同的线程上运行,这让我想知道,我是否需要同步机制(如显式屏障)来确保值同步以避免此处描述的问题:http://www.albahari.com/threading/part4.aspx
编辑 2:在 stateless 上也有一个小讨论: https://github.com/dotnet-state-machine/stateless/issues/294
最佳答案
I don't see what is making sure that the _firing and _eventQueue fields are synchronized, for example what is creating the a lock/memory-fence/barrier here? So my question is; do I need to make the fields thread-safe, or is something in the async/await structure taking care of this?
await
将确保所有必要的内存屏障都到位。然而,这并不能使它们成为“线程安全的”。
in this case InternalFireQueuedAsync should always be called on the same thread.
然后_firing
很好,不需要 volatile
或类似的东西。
但是,_eventQueue
的用法是不正确的。考虑当线程池线程在 await
之后恢复代码时会发生什么: 完全有可能 Queue<T>.Count
或 Queue<T>.Dequeue()
将同时被一个线程池线程调用Queue<T>.Enqueue
被主线程调用。这不是线程安全的。
如果主线程调用InternalFireQueuedAsync
是具有单线程上下文的线程(例如 UI 线程),那么一个简单的解决方法是删除 ConfigureAwait(false)
的所有实例在这个方法中。
关于c# - 使用 async/await 时是否需要使字段线程安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53649448/