C++ C+中的原子性+;:神话还是现实

C++ C+中的原子性+;:神话还是现实,c++,atomic,memory-alignment,C++,Atomic,Memory Alignment,我一直在读一篇关于MSDN的文章。它说: 在所有现代处理器上,您都可以 假设读写 自然对齐的本机类型是原子的。只要内存总线是 至少与所选类型一样宽 读或写,CPU读和写 在单个总线中写入这些类型 交易,使交易无法进行 其他线程以在 半完成状态 并举例说明: // This write is not atomic because it is not natively aligned. DWORD* pData = (DWORD*)(pChar + 1); *pData = 0; // This

我一直在读一篇关于MSDN的文章。它说:

在所有现代处理器上,您都可以 假设读写 自然对齐的本机类型是原子的。只要内存总线是 至少与所选类型一样宽 读或写,CPU读和写 在单个总线中写入这些类型 交易,使交易无法进行 其他线程以在 半完成状态

并举例说明:

// This write is not atomic because it is not natively aligned.
DWORD* pData = (DWORD*)(pChar + 1);
*pData = 0;

// This is not atomic because it is three separate operations.
++g_globalCounter;

// This write is atomic.
g_alignedGlobal = 0;

// This read is atomic.
DWORD local = g_alignedGlobal;
我读了很多答案和评论,说C++中没有任何东西是原子的,甚至在标准技术中也没有提到,所以现在我有点困惑。我是不是误解了这篇文章?或者,文章作者是否讨论了MSVC++编译器特有的非标准问题

所以根据这篇文章,下面的赋值必须是原子的,对吗

struct Data
{
    char ID;
    char pad1[3];
    short Number;
    char pad2[2];
    char Name[5];
    char pad3[3];
    int Number2;
    double Value;
} DataVal;

DataVal.ID = 0;
DataVal.Number = 1000;
DataVal.Number2 = 0xFFFFFF;
DataVal.Value = 1.2;
如果为真,是否将
Name[5]
pad3[3]
替换为
std::string Name在内存对齐方面有什么不同吗?对
Number2
Value
变量的赋值是否仍然是原子的


可以解释一下吗?

< P> C++标准并不能保证原子行为。但实际上,正如本文所述,简单的加载和存储操作将是原子的

如果您需要原子性,那么最好明确说明它并使用某种锁

*counter = 0; // this is atomic on most platforms
*counter++;   // this is NOT atomic on most platforms

在我看来,这篇文章包含了一些关于底层架构的假设。由于C++对体系结构只有一些极简主义的要求,因此在标准中不能保证关于原子性的例子。例如,一个字节必须至少是8位,但你可以有一个体系结构,其中一个字节是9位,而一个整数是16位。。。理论上

因此,当编译器特定于x86架构时,可以使用特定的功能


注意:默认情况下,结构通常与本机单词边界对齐。您可以通过#pragma语句禁用该功能,因此不需要填充。

在依赖简单字号操作的原子性时要非常小心,因为事情的行为可能与您预期的不同。在多核体系结构上,您可能会看到无序读写。这将需要内存屏障来防止。(更多细节)


应用程序开发人员的底线是要么使用操作系统保证是原子的原语,要么使用适当的锁。

我认为他们试图获得的是硬件本机实现的数据类型,在硬件中进行更新,这样从另一个线程读取的数据将永远不会给出“部分”更新值

考虑32位以上机器上的32位整数。它在1个指令周期内完全写入或读取,而较大的数据类型(例如32位机器上的64位int)将需要更多的周期,因此理论上,在这些周期之间写入它们的线程可能会中断,因为值处于无效状态

不使用字符串不会使其原子化,因为字符串是更高级别的构造,并且没有在硬件中实现。 编辑:根据您对更改为字符串的意思的评论,它不应该对后面声明的字段产生任何影响,正如在另一个答案中提到的那样,编译器将默认对齐字段


它不在标准中的原因是,如本文所述,这是关于现代处理器如何实现指令的。您的标准C/C++代码在16位或64位机器上的工作方式应该完全相同(只是性能不同),但是如果您假设您只在64位机器上执行,那么任何64位或更小的代码都是原子的。(撇开SSE etc类型不谈)

我不认为将
字符名[5]
更改为
std::string Name
会产生不同如果您仅将其用于单个字符分配,因为索引操作符将返回对底层字符的直接引用。完整的字符串分配不是原子的(而且不能用字符数组进行分配,所以我猜您不会考虑以这种方式使用它)。

此建议是特定于体系结构的。x86和x86_64(在低级编程中)也是如此。您还应该检查编译器是否没有对代码重新排序。您可以使用“编译器内存屏障”来实现这一点

英特尔参考手册“英特尔64和IA-32体系结构软件开发人员手册”第3A卷()第8.1.1节介绍了x86的低级原子读写

8.1.1保证的原子操作

Intel486处理器(以及此后更新的处理器)保证: 基本内存操作将始终以原子方式执行:

  • 读或写一个字节
  • 读或写在16位边界上对齐的字
  • 读取或写入在32位边界上对齐的双字
奔腾处理器(以及此后的较新处理器)保证: 附加内存操作将始终以原子方式执行:

  • 读取或写入64位边界上对齐的四字
  • 对32位数据总线中未缓存内存位置的16位访问
P6系列处理器(以及此后的较新处理器)保证以下各项 附加内存操作将始终以原子方式执行:

  • 对适合缓存的缓存内存进行未对齐的16、32和64位访问 线
本文档还对Core2等较新处理器的原子性进行了更多描述。并非所有未对齐的操作都是原子操作

其他英特尔手册推荐本白皮书:


我认为你误解了这句话

原子性可以使用特定指令(适用于此体系结构)在给定体系结构上得到保证。这个