如何解决std::atomic:当任务循环时内存屏障是否存在?
所以说我有两个任务同时运行的构造(伪代码):
int a,b,c;
std::atomic<bool> flag;
TaskA()
{
while (1) {
a = 5;
b = 2;
c = 3;
flag.store(true,std::memory_order_release);
}
}
TaskB()
{
while (1) {
flag.load(std::memory_order_acquire);
b = a;
c = 2*b;
}
}
内存屏障应该在标志变量处。据我了解,这意味着 TaskB (b = a and c = 2b) 中的操作是在 TaskA (a = 5,b = 2,c = 3) 中的赋值之后执行的。但这是否也意味着,当 TaskB 仍然处于 c = 2*b 时,TaskA 不可能已经循环并再次执行 b = 2 ?这是否以某种方式阻止了,我们是否需要在循环开始时使用第二个屏障?
解决方法
如果您在发布存储之后立即开始另一个非原子变量的写入,则再多的障碍也无法帮助您避免数据竞争 UB。
当您的读者正在阅读这些变量时,对 a
、b
和 c
的一些非原子写入总是可能(并且很可能)“发生”,因此,在 C 抽象机中,您有数据竞争 UB。 (在您的示例中,从 a
的未同步写入+读取、b
的未同步写入+写入、b
的写入+读取和 c
的写入+写入.)
此外,即使没有循环,您的示例仍然无法安全地避免数据竞争 UB,因为您的 TaskB 访问 a
、b
和 c
在 flag.load
之后无条件。因此,无论您是否观察到来自写入器的 data_ready = 1 信号,表明 var 已准备好被读取,您都会执行这些操作。
当然在实际实现中,重复写入相同的数据不太可能在这里引起问题,除了 b
读取的值将取决于编译器的优化方式。但那是因为你的例子也写了。
主流 CPU 没有硬件竞争检测,所以它实际上不会出错或其他什么,如果你真的等待 flag==1
然后只是读取,你会看到预期值,即使作者正在运行更多相同值的分配。 (DeathStation 9000 可以通过在该空间中临时存储其他内容来实现这些分配,因此内存中的字节实际上正在发生变化,在第一次发布存储之前不是稳定的值副本,但这不是您期望真正的编译器做。不过,我不会打赌,这似乎是一种反模式)。
这就是无锁队列使用多个数组元素的原因,或者 seqlock 不能以这种方式工作的原因。 (seqlock can't be implemented both safely and efficiently in ISO C++ 因为它依赖于读取可能被撕裂的数据,然后检测撕裂;如果您对数据块使用足够窄的放松原子,则会损害效率。)
想要再次写作的整个想法,也许在读者完成阅读之前,听起来很像您应该研究 SeqLock 的想法。 https://en.wikipedia.org/wiki/Seqlock 并在最后一段的链接答案中查看其他链接。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。