我仍然对以原子方式读取和写入变量感到困惑。对于那些试图在以前的问题上提供帮助的人,请提前抱歉。
我被告知today在读取和写入32位Interlocked
值时,不需要进行任何long
函数调用,这会使我的previous beliefs值出窗口。实际上,这支持MSDN所说的
对正确对齐的32位变量进行简单的读写操作 原子操作。换句话说,你不会只有一个 变量的一部分更新;所有位都以原子方式更新 时尚
然后我看看VS2017中atomic_long
的作用;其operator=
使用新值对当前值执行_InterlockedExchange()
。
这与MSDN不矛盾吗?如果读/写是原子的,为什么需要这个Interlocked函数?
在同一个MSDN页面上,我们还有
对正确对齐的64位变量进行简单的读写操作 64位Windows上的原子。对64位值的读写不是 保证在32位Windows上是原子的。 读取和写入 任何其他大小的变量都不保证是原子的 平台。
然后我进入atomic_bool
' operator=
;它会执行_InterlockedExchange8
调用MSDN告诉我只能在Windows 8及更高版本上使用。
这似乎备份了第二个MSDN引用。
如果我使用VS 2005并针对Windows XP,我如何确保布尔变量的原子读/写?我是否必须使用互斥锁或类似的?
如果读/写操作不是原子操作,则容易被撕裂;这是正确的吗?
我读过this PAQ,说原始类型不是原子的。这再次与我在问题中告诉我的内容以及MSDN告诉我的内容相矛盾。谁是对的?
答案 0 :(得分:2)
嗯,你引用了文档的摘录,但没有引用它之后的句子:
但是,无法保证访问同步。如果两个线程正在从同一个变量读取和写入,则无法确定一个线程是否会在另一个线程执行其写入操作之前执行其读取操作。
也就是说,虽然操作是原子的,即一个线程保证写入所有4个字节,但在另一个允许读取它之前,这并不意味着你将得到正确的值,并且顺序为执行未知。
Interlocked函数确保操作以“事务”方式执行,即它将是原子的,并且所有线程/处理器将获得最后更新的值 - 否则由于缓存(每个都不能保证) CPU /核心可以在其本地缓存中维护副本。 Interlocked函数以及所有同步函数确保缓存内容同步。
要读取/更新任何共享数据,是的,您需要使用互斥锁。对于同一进程的线程,一个关键部分更可取,因为它非常快(不可测量的时间跨度,用于确保屏幕输出操作的原子性,所以我可以确认这一点)。
答案 1 :(得分:1)
我今天被告知在读取和写入32位长值时不需要任何互锁函数调用
因为它不是真的,至少不是那种通用的。
仅适用于x86,并且仅当32位值类型对齐时,并且该变量实际上使用单个32位宽指令进入内存。这通常是这种情况,但绝不保证,编译器可能决定优化或不能假设对齐并且因此无法保证对齐和原子存储。
这与MSDN没有矛盾;如果读/写是原子的,为什么需要这个Interlocked函数?
atomic_long
未按定义对齐,因此正式更正需要原子交换。
此函数生成一个完整的内存屏障(或栅栏),以确保按顺序完成内存操作。
这是关于原子的另一个相关部分 - 它们形成了记忆障碍。
这意味着编译器和处理器都不能重新排序内存访问。如果你有例如您在同一范围内独立编写的两个普通int
变量,编译器可以随意批量处理或重新排序写入。
作为副作用,处理器还将尝试同步缓存,因此您将更快地看到对另一个核心上的原子变量所做的更新。
这超出了普通volatile
的效果,它也会阻止重新排序内存访问,但仍可能需要花费几微秒等待内存写入在另一个内核上刷新。
如果我使用VS 2005并针对Windows XP,我如何确保布尔变量的原子读/写?我是否必须使用互斥锁或类似的?
是。好吧,写入是原子的,无论哪种方式,但如果你一直使用布尔来表示另一个存储区域现在可以安全访问(例如自旋锁),你真的想要内存屏障。
如果读/写操作不是原子操作,则容易被撕裂;那是对的吗?
不适用于bool
,因为它实际上只是一个字节,而且我不知道任何可能会破坏子字节级变量的架构。
对于所有较大的类型,是的,因为无法保证对齐。如前所述,你不能保证编译器使用原子指令,即使它是免费的#34;