使用异步/等待时是否需要使字段成为线程安全的? [英] Is it needed to make fields thread-safe when using async/await?
问题描述
有时,我会遇到访问对象字段的异步/等待代码.例如,该代码段来自无状态项目的代码:
Sometimes I encounter async/await code that accesses fields of an object. For example this snippet of code from the Stateless project:
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
之后执行的代码确实
If I understand correctly the await **.ConfigureAwait(false)
indicates that the code that is executed after this await
does not necessarily has to be executed on the same context. So the while
loop here could be executed on a ThreadPool thread. 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?
以澄清我的问题;在这种情况下,应始终在同一线程上调用 InternalFireQueuedAsync
.在那种情况下,只有延续可以在不同的线程上运行,这让我感到奇怪,我是否需要同步机制(例如显式屏障)来确保值被同步以避免出现此处描述的问题:
to clarify my question; in this case InternalFireQueuedAsync
should always be called on the same thread. In that case only the continuation could run on a different thread, which makes me wonder, do I need synchronization-mechanisms(like an explicit barrier) to make sure the values are synchronized to avoid the issue described here: http://www.albahari.com/threading/part4.aspx
在无状态下也有一个小讨论: https://github.com/dotnet-state-machine/stateless/issues/294
Edit 2: there is also a small discussion at stateless: https://github.com/dotnet-state-machine/stateless/issues/294
推荐答案
我看不到是什么确保_firing和_eventQueue字段是同步的,例如,是什么在这里创建了lock/memory-fence/barrier?所以我的问题是;我需要使字段成为线程安全的,还是async/await结构中的某些东西来解决这个问题?
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
将确保所有必要的内存屏障均已到位.但是,这并不能使它们具有线程安全性".
await
will ensure all necessary memory barriers are in place. However, that doesn't make them "thread-safe".
在这种情况下,应始终在同一线程上调用InternalFireQueuedAsync.
in this case InternalFireQueuedAsync should always be called on the same thread.
然后 _firing
很好,不需要 volatile
或类似的东西.
Then _firing
is fine, and doesn't need volatile
or anything like that.
但是, _eventQueue
的用法不正确.考虑当线程池线程在 await
之后恢复代码时会发生什么:完全有可能 Queue< T> .Count
或 Queue< T>.Dequeue()
将由线程池线程同时调用 Queue< T> .Enqueue
由主线程调用.这不是线程安全的.
However, the usage of _eventQueue
is incorrect. Consider what happens when a thread pool thread has resumed the code after the await
: it is entirely possible that Queue<T>.Count
or Queue<T>.Dequeue()
will be called by a thread pool thread at the same time Queue<T>.Enqueue
is called by the main thread. This is not threadsafe.
如果调用 InternalFireQueuedAsync
的主线程是具有单线程上下文的线程(例如UI线程),那么一个简单的解决方法是删除 ConfigureAwait(false)
.
If the main thread calling InternalFireQueuedAsync
is a thread with a single-threaded context (such as a UI thread), then one simple fix is to remove all the instances of ConfigureAwait(false)
in this method.
这篇关于使用异步/等待时是否需要使字段成为线程安全的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!