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

ali*_*hoo 33 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 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;
Run Code Online (Sandbox Code Playgroud)

我读了很多答案和评论说,没有什么能保证在C++中是原子的,甚至在标准中都没有提到,在SO中,现在我有点困惑.我误解了这篇文章吗?或者文章作者是否谈论了非标准和特定于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;
Run Code Online (Sandbox Code Playgroud)

如果确实如此,那么替换Name[5]pad3[3]std::string Name;内存对齐有什么不同吗?赋值Number2Value变量仍然是原子的吗?

有人可以解释一下吗?

osg*_*sgx 29

此建议特定于体系结构.对于x86和x86_64(在低级编程中)也是如此.您还应检查编译器是否重新排序代码.您可以使用"编译器内存屏障".

英特尔参考手册"英特尔®64和IA-32架构软件开发人员手册"第3A卷(http://www.intel.com/Assets/PDF/manual/253668)中介绍了x86的低级原子读写操作. pdf),第8.1.1节

8.1.1保证原子操作

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

  • 读或写一个字节
  • 读取或写入在16位边界上对齐的字
  • 读取或写入在32位边界上对齐的双字

奔腾处理器(以及更新的处理器)保证以下额外的内存操作将始终以原子方式执行:

  • 读取或写入在64位边界上对齐的四字
  • 16位访问非缓存内存位置,适合32位数据总线

P6系列处理器(以及之后的新处理器)保证以下额外的内存操作将始终以原子方式执行:

  • 未对齐的16位,32位和64位访问缓存内存,适合缓存行

对于像Core2这样的新处理器,本文档还有更多原子级的描述.并非所有未对齐的操作都是原子的.

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

http://software.intel.com/en-us/articles/developing-multithreaded-applications-a-platform-consistent-approach/


Mat*_* M. 12

我认为你误解了这句话.

使用特定指令(适用于此架构),可以在给定体系结构上保证原子性.MSDN文章解释说,C++内置类型的读写可以预期在架构上是原子的x86.

但是,C++标准并没有假定架构是什么,因此标准不能做出这样的保证.实际上,C++用于嵌入式软件,其中硬件支持更加有限.

C++ 0x定义了std::atomic模板类,它允许将读取和写入转换为原子操作,无论何种类型.编译器将根据类型特征和以标准兼容方式定向的体系结构选择获得原子性的最佳方法.

新标准还定义了许多类似于MSVC的操作,InterlockExchange这些操作也被编译为硬件提供的最快(但安全)可用的原语.