c# - 使用 async/await 时是否需要使字段线程安全?
问题描述
有时我会遇到访问对象字段的异步/等待代码。例如,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
不一定必须在相同的上下文中执行。所以while
这里的循环可以在 ThreadPool 线程上执行。我看不到是什么确保_firing
和_eventQueue
字段同步,例如什么在这里创建了锁/内存围栏/屏障?所以我的问题是;我是否需要使字段线程安全,或者异步/等待结构中的某些东西可以解决这个问题?
编辑:澄清我的问题;在这种情况下InternalFireQueuedAsync
,应该始终在同一个线程上调用。在那种情况下,只有延续可以在不同的线程上运行,这让我想知道,我是否需要同步机制(如显式屏障)来确保值同步以避免此处描述的问题:http://www. albahari.com/threading/part4.aspx
编辑2:在无状态也有一个小讨论: https ://github.com/dotnet-state-machine/stateless/issues/294
解决方案
我看不到是什么确保了 _firing 和 _eventQueue 字段是同步的,例如什么在这里创建了锁/内存围栏/屏障?所以我的问题是;我是否需要使字段线程安全,或者异步/等待结构中的某些东西可以解决这个问题?
await
将确保所有必要的内存屏障都到位。但是,这并不能使它们“线程安全”。
在这种情况下,应始终在同一线程上调用 InternalFireQueuedAsync。
然后_firing
很好,不需要volatile
或类似的东西。
但是,的用法_eventQueue
不正确。考虑当线程池线程在 之后恢复代码时会发生什么await
:完全有可能Queue<T>.Count
或Queue<T>.Dequeue()
将被线程池线程调用,同时Queue<T>.Enqueue
被主线程调用。这不是线程安全的。
如果主线程调用InternalFireQueuedAsync
是具有单线程上下文的线程(例如 UI 线程),那么一个简单的解决方法是删除ConfigureAwait(false)
此方法中的所有实例。
推荐阅读
- java - 使用Kafka消费者池是否正确?
- python - Scikit-learn:在超参数调整后对整个数据集使用交叉验证
- php - 按 ID 过滤 XML 文件
- ios - JSON 有问题 - 数据重复
- objective-c - 在 Objective-C 生成的代码中 Protobuf 缺少“列表”后缀
- twilio - Twilio 合并子录音
- javascript - 如何正确递归地遍历磁盘目录并使用node.js将所有相对格式的路径流式传输到json中?
- javascript - 使用 reducer 更新 Redux 中的特定状态
- javascript - 考虑到动态内容高度,使用 vue 过渡平滑展开/折叠过渡
- node.js - 找不到ffprobe?