我有两个线程,一个线程更新一个int,另一个读取它。这是一个统计值,其中读取和写入的顺序无关紧要。
我的问题是,是否仍然需要同步对此多字节值的访问?或者换一种说法,可以完成部分写入并使其中断,然后进行读取。
例如,考虑一个值为0x0000FFFF的值,该值的增量值为0x00010000。
是否有一段时间我应该担心该值看起来像0x0001FFFF?当然,类型越大,越可能发生这种情况。
我一直同步这些类型的访问,但是很好奇社区的想法。
我有两个线程,一个线程更新一个int,另一个读取它。这是一个统计值,其中读取和写入的顺序无关紧要。
我的问题是,是否仍然需要同步对此多字节值的访问?或者换一种说法,可以完成部分写入并使其中断,然后进行读取。
例如,考虑一个值为0x0000FFFF的值,该值的增量值为0x00010000。
是否有一段时间我应该担心该值看起来像0x0001FFFF?当然,类型越大,越可能发生这种情况。
我一直同步这些类型的访问,但是很好奇社区的想法。
Answers:
最初,人们可能会认为对本机机器大小的读取和写入是原子的,但是要处理许多问题,包括处理器/内核之间的缓存一致性。在Windows上使用原子操作,例如Interlocked *,在Linux上使用等效操作。C ++ 0x将具有一个“原子”模板,以将它们包装在一个不错的跨平台界面中。现在,如果您正在使用平台抽象层,它可能会提供这些功能。 ACE可以,请参见类模板ACE_Atomic_Op。
男孩,什么问题。答案是:
是的,不,嗯,这取决于
一切都取决于系统的体系结构。在IA32上,正确对齐的地址将是原子操作。未对齐的写入可能是原子的,这取决于所使用的缓存系统。如果内存位于单个L1缓存行中,则它是原子的,否则就不是。CPU和RAM之间的总线宽度可能会影响原子性质:在8086上正确对齐的16位写入是原子的,而在8088上进行相同的写入则不是,因为8088仅具有8位总线,而8086却具有8位总线。 16位总线。
同样,如果您使用的是C / C ++,请不要忘记将共享值标记为volatile,否则优化程序会认为该变量永远不会在您的线程之一中更新。
如果您正在读取/写入4字节的值,并且它在内存中是DWORD对齐的,并且您正在I32体系结构上运行,则读写是原子的。
您必须同步,但是在某些体系结构上,有进行同步的有效方法。
最好是使用子例程(可能在宏后面隐藏),以便您可以有条件地将实现替换为特定于平台的实现。
Linux内核已经有一些这样的代码。
不,它们不是(或者至少您不能认为它们是)。话虽如此,有一些技巧可以自动完成,但是它们通常是不可移植的(请参阅Compare-and-swap)。
唯一可移植的方法是为编译器使用signal.h标头中定义的sig_atomic_t类型。在大多数C和C ++实现中,这是一个整数。然后将变量声明为“ volatile sig_atomic_t”。
有人认为++ c是原子的,但是关注生成的程序集。例如,'gcc -S':
movl cpt.1586(%rip), %eax
addl $1, %eax
movl %eax, cpt.1586(%rip)
要增加一个int,编译器首先将其加载到寄存器中,然后将其存储回内存中。这不是原子的。
绝对不行!我们最高的C ++权威M. Boost给出的答案
不保证对“普通”变量的操作是原子的。
arithmetic
其中包括在“普通”变量读取,更新,写序的操作不是原子,而不是是否read
或者write
在“普通”变量操作是原子或没有。