当然,为了使典型的现代处理器体系结构(如x86_64)执行原子加载或存储,需要对齐要读/写的数据.
但是这个要求是如何通过C++ 11 <atomic>变量实际实现/实施的呢?
假设我有一个支持16字节比较和交换(双字CAS)的架构,因此它可以原子地读/写16字节值,并且我定义了一个16字节类型:
struct double_word
{
std::uint64_t x;
std::uint64_t y;
};
Run Code Online (Sandbox Code Playgroud)
现在,假设我包含std::atomic<double_word>一个类的成员字段:
class foo
{
public:
std::atomic<double_word> dword;
};
Run Code Online (Sandbox Code Playgroud)
我如何知道foo::dword实际上是在16字节边界上对齐?我怎么知道调用dword.load()实际上是原子的?
实际上,我最初开始问这个问题是因为我之前添加了另一个数据成员时发生的奇怪事情foo::dword.我定义foo为:
class foo
{
public:
std::uint64_t x;
std::atomic<double_word> dword;
};
Run Code Online (Sandbox Code Playgroud)
当我foo::dword在运行Debian Linux的x86_64机器上实际执行原子加载,并使用GCC 4.7.2编译和运行时,它实际上给了我一个分段错误!
完整计划:
#include <atomic>
#include <cstdint>
struct double_word
{
std::uint64_t x;
std::uint64_t y;
};
class foo
{
public:
std::uint64_t x;
std::atomic<double_word> dword; // <-- not aligned on 16-byte boundary
};
int main()
{
foo f;
double_word d = f.dword.load(); // <-- segfaults with GCC 4.7.2 !!
}
Run Code Online (Sandbox Code Playgroud)
这实际上是段错误f.dword.load().起初我不明白为什么,但后来我意识到,dword是不是一个16字节边界上对齐.因此,这导致了许多问题:如果原子变量未对齐并且我们尝试以原子方式加载它,编译器应该怎么做?是不确定的行为?为什么程序只是段错误?
其次,C++ 11标准对此有何看法?编译器是否应确保double_word在16字节边界上自动对齐?如果是这样,这是否意味着GCC在这里只是错误?如果不是 - 看起来用户需要确保对齐,在这种情况下,任何时候我们使用std::atomic<T>大于一个字节,似乎我们必须使用std::aligned_storage它来确保它正确对齐,(A)似乎繁琐的,(B)是我在实践中或在任何示例/教程中从未真正看到过的东西.
那么,使用C++ 11的程序员应如何<atomic>处理这样的对齐问题呢?