在C11/C++ 11中,可以在同一个内存中混合原子/非原子操作吗?

Jos*_*man 9 c c++ concurrency multithreading c++11

是否可以在同一个内存位置执行原子操作和非原子操作?

我问不是因为我真的想这样做,而是因为我试图理解C11/C++ 11内存模型.他们定义了一个"数据竞争",如下所示:

程序的执行包含数据竞争,如果它在不同的线程中包含两个冲突的动作,其中至少有一个不是原子的,并且都不会在另一个之前发生.任何此类数据争用都会导致未定义的行为.- C11 §5.1.2.4P25,C++ 11 §1.10 p21基因

它的"至少有一个不是原子的"是困扰我的部分.如果不可能混合原子和非原子操作,它只会说"在一个非原子的物体上".

我看不到对原子变量执行非原子操作的任何直接方式. std::atomic<T>在C++中,没有使用非原子语义定义任何操作.在C中,原子变量的所有直接读/写似乎都被转换为原子操作.

我想memcpy()和其他直接内存操作可能是对原子变量执行非原子读/写的方式吗?即.memcpy(&atomicvar, othermem, sizeof(atomicvar))?但这是否定义为行为?在C++中,std::atomic是不可复制的,那么它是否会memcpy()在C或C++中定义它的行为?

原子变量的初始化(无论是通过构造函数还是atomic_init())被定义为不是原子的.但这是一次性操作:您不允许第二次初始化原子变量.放置新的或显式的析构函数调用也可能不是原子的.但是在所有这些情况下,似乎不会定义行为,因为有一个可能在未初始化值上运行的并发原子操作.

对非原子变量执行原子操作似乎完全不可能:C和C++都没有定义任何可以对非原子变量进行操作的原子函数.

那么这里的故事是什么?是真的memcpy(),或初始化/破坏,还是其他什么?

MSa*_*ers 1

我认为你忽略了另一种情况,即相反的顺序。考虑一个已初始化的int对象,其存储被重用以创建std::atomic_int. 所有原子操作都在其构造函数完成后发生,因此在初始化的内存上发生。但对现在已被覆盖的内容的任何并发、非原子访问int也必须被禁止。

(我在这里假设存储寿命足够并且不起作用)

我不完全确定,因为我认为第二次访问int无论如何都是无效的,因为访问表达式的类型与int当时对象的类型不匹配(std::atomic<int>)。然而,“当时对象的类型”假设单个线性时间进程,这在多线程环境中不成立。一般来说,C++11 通过对“全局状态”未定义行为本身做出这样的假设来解决这个问题并且问题中的规则似乎适合该框架。

因此,也许换句话来说:如果单个内存位置包含一个原子对象以及一个非原子对象,并且如果最早创建的(较旧的)对象的销毁没有在创建另一个(较新的)对象之前排序,那么对旧对象的访问与对新对象的访问发生冲突,除非前者被安排在后者之前。