在同一地址具有2个std :: atomic变量的两个不同过程?

时间:2018-07-08 05:49:14

标签: c++ c++11

我阅读了C ++ Standard(n4713)的§32.6.1 3:

  

无锁的操作也应无地址。那是,   通过两个不同的原子对同一内存位置进行原子操作   地址将自动进行通信。实施不应该   取决于任何按进程的状态。此限制使   内存通信多次映射到一个进程   以及两个进程之间共享的内存。

因此,听起来可以在同一存储位置上执行无锁原子操作。我不知道该怎么办。

假设我在Linux上有一个命名的共享内存段(通过shm_open()和mmap())。例如,如何在共享内存段的前4个字节上执行无锁操作?

起初,我以为我可以reinterpret_cast指向std::atomic<int32_t>*的指针。但是后来我读了this。首先指出std :: atomic的T或对齐方式可能不相同:

  

当我们设计C ++ 11原子时,我被误认为   可以半便携式地将原子操作应用于数据   使用诸如

之类的代码未声明为原子的
int x; reinterpret_cast<atomic<int>&>(x).fetch_add(1);
  

如果atomic和int的表示形式显然会失败   不同,或者它们的对齐方式不同。但是我知道这不是   我关心的平台上的问题。而且,在实践中,我可以轻松地进行测试   通过在编译时检查大小和对齐方式来解决问题   匹配。

在这种情况下,这对我很好,因为我在同一台计算机上使用共享内存,并且在两个不同进程中强制转换指针将“获取”相同的位置。但是,该文章指出,编译器可能不会将强制转换的指针视为指向原子类型的指针:

  

但是,即使在以下平台上,也不能保证这是可靠的   可能会期望它起作用,因为它可能会使基于类型的混淆   编译器中的别名分析。编译器可能会假设int为   也不能作为atomic<int>访问。 (请参见最后一个3.10 [Basic.lval]   段落。

欢迎任何输入!

3 个答案:

答案 0 :(得分:4)

C ++标准本身并不涉及多个进程,因此不会有任何正式的答案。这个答案将假定程序在同步方面的行为与线程在某种程度上相同。

第一个解决方案需要C ++ 20 atomic_ref

void* shared_mem = /* something */

auto p1 = new (shared_mem) int;  // For creating the shared object
auto p2 = (int*)shared_mem;      // For getting the shared object

std::atomic_ref<int> i{p2};      // Use i as if atomic<int>

这可以防止共享内存中存在不透明的原子类型,从而使您可以精确控制其中的确切内容。

C ++ 20之前的解决方案是

auto p1 = new (shared_mem) atomic<int>;  // For creating the shared object
auto p2 = (atomic<int>*)shared_mem;      // For getting the shared object

auto& i = *p2;

或使用C11 atomic_loadatomic_store

volatile int* i = (volatile int*)shared_mem;
atomic_store(i, 42);
int i2 = atomic_load(i);

答案 1 :(得分:1)

是的,对于所有这些,C ++标准有点meal嘴。

如果您使用的是Windows(可能不是),则可以使用InterlockedExchange()等,它提供了所有必需的语义,并且不在乎所引用的对象在哪里(这是一个LONG *)。

在其他平台上,gcc有一些atomic builtins可能对此有所帮助。他们可能使您摆脱标准编写者的暴政。麻烦的是,很难测试生成的代码是否是防弹的。

答案 2 :(得分:1)

在所有主流平台上,std::atomic<T>确实具有与T相同的大小,但是如果T具有alignof

您可以使用以下方法检查这些假设:

  static_assert(sizeof(T) == sizeof(std::atomic<T>), 
            "atomic<T> isn't the same size as T");

  static_assert(std::atomic<T>::is_always_lock_free,  // C++17
            "atomic<T> isn't lock-free, unusable on shared mem");

  auto atomic_ptr = static_cast<atomic<int>*>(some_ptr);
           // beware strict-aliasing violations
           // don't also access the same memory via int*
           // unless you're aware of possible issues
      // also make sure that the ptr is aligned to alignof(atomic<T>)
      // otherwise you might get tearing (non-atomicity)

在并非如此的奇异C ++实现中,想要在共享内存上使用您的代码的人将需要做其他事情。

或者如果 all 从所有进程始终使用atomic<T>访问共享内存,那没有问题,您只需要无锁来保证无地址。 (您确实需要检查以下内容:std :: atomic将锁的哈希表用于非无锁。这取决于地址,并且单独的进程将具有单独的锁的哈希表。)