简体   繁体   English

在这种特殊情况下,我是否需要共享内存中的原子类型?

[英]Do I need an atomic type in shared memory in this particular case?

I have multiple processes using boost shared memory. 我有多个进程使用boost共享内存。 The writer processes will write to an array in shared memory like so: 编写器进程将写入共享内存中的数组,如下所示:

void push(int32_t val_)
{
    int nextIndex = _currentIndex.fetch_add(1,std::memory_order_relaxed);
    _buffer[nextIndex] = val_;
}
//val_ is guaranteed to be >=1
//_buffer is an array of int32_t in shared memory initialized to 0

The single reader process will read like so: 单个读者进程将如下所示:

void process()
{
    int idx=0;
    while(running)
    {
      int32_t val = _buffer[idx];
      if(val)
      {
          //do some work...
          ++idx;
      }
    }
}            

According to boost: "Changes in that address range are automatically seen by other process that also have mapped the same shared memory object." 根据提升:“地址范围的变化会自动被其他进程看到,这些进程也映射了相同的共享内存对象。”

My question is, assuming _buffer is aligned properly, can _buffer simply be an array of int32_t or is it absolutely necessary to define _buffer as an array of std::atomic ? 我的问题是,假设_buffer正确对齐,_buffer可以只是一个int32_t数组,还是绝对有必要将_buffer定义为std :: atomic数组? Writing to int32_t is atomic on x86 assuming alignment is correct and boost guarantees other processes will see the update. 写入int32_t在x86上是原子的,假设对齐是正确的,并且boost保证其他进程将看到更新。

CPU info: CPU信息:

Architecture:          x86_64
CPU op-mode(s):        32-bit, 64-bit
Byte Order:            Little Endian
CPU(s):                24
On-line CPU(s) list:   0-23
Thread(s) per core:    1
Core(s) per socket:    12
Socket(s):             2
NUMA node(s):          2
Vendor ID:             GenuineIntel
CPU family:            6
Model:                 63
Stepping:              2
CPU MHz:               2596.945
BogoMIPS:              5193.42
Virtualization:        VT-x
L1d cache:             32K
L1i cache:             32K
L2 cache:              256K
L3 cache:              30720K
NUMA node0 CPU(s):     0-5,12-17
NUMA node1 CPU(s):     6-11,18-23

As you wrote yourself, one thread writes to some memory location: 正如你自己写的那样,一个线程写入一些内存位置:

_buffer[nextIndex] = val_;

And a different thread reads that memory location: 另一个线程读取内存位置:

  int32_t val = _buffer[idx];

According to the standard, that memory address has to be synchronized or else it's undefined behaviour. 根据标准,该内存地址必须同步,否则它是未定义的行为。

That array has to be either an array of atomics, a simple array protected by a mutex or a fast spinlock or any other array that its reads and writes are synchronized. 该数组必须是原子数组,由互斥锁或快速自旋锁保护的简单数组或其读写同步的任何其他数组。

TL;DR : Even then, you do need to synchronize your accesses. TL; DR :即使这样,您也需要同步访问。 And use atomics, not ordinary mutexes. 并使用原子,而不是普通的互斥体。

There are a few of issues here: 这里有一些问题:

First, you have a writer thread and a reader thread, writing to and reading from the same memory location. 首先,您有一个编写器线程和一个读取器线程,写入和读取相同的内存位置。 This means that access to that location must be protected (using locks, atomic operations, fences, whatever.) The fact that these threads are in different processes doesn't enter into it. 这意味着必须保护对该位置的访问(使用锁,原子操作,围栏等)。这些线程在不同进程中的事实不会进入它。 You still need to resolve the data race. 您仍然需要解决数据争用问题。

Second, although the Boost documentation says that changes are automatically seen by other processes that map the region, but I believe that's just a simplification. 其次,尽管Boost文档说其他映射该区域的进程会自动看到更改,但我认为这只是一种简化。 The shared memory library cannot provide stronger guarantees about shared memory among different processes than those that exist for different threads in the same process. 共享内存库不能为不同进程之间的共享内存提供比同一进程中不同线程存在的更强的保证。 All the same problems may still arise: your reads/writes might be reordered by the compiler or the CPU or even the memory system, or even omitted completely or combined with other reads/writes. 所有相同的问题仍可能出现:您的读/写可能由编译器或CPU甚至内存系统重新排序,甚至完全省略或与其他读/写组合。 And there are cache and MMU effects to be considered. 并且需要考虑缓存和MMU效果。

So even though your data is correctly aligned, and writes to that datatype are atomic on your architecture, it doesn't provide any safety against incorrect behavior resulting from data race if you don't protect your accesses. 因此,即使您的数据已正确对齐,并且对该数据类型的写入在您的体系结构中是原子的,但如果您不保护访问,则它不会对数据争用导致的错误行为提供任何安全性。 There is no magic here; 这里没有魔力; if you had to synchronize/protect/atomicize your accesses when you had threads, you need to do the same for processes as well. 如果在有线程时必须同步/保护/原子化访问,则还需要对进程执行相同操作。 You probably have to do even more. 你可能还需要做更多。

Third, the synchronization primitives that work for threads inside the same process might not (and probably will not) work across different processes. 第三,适用于同一进程内的线程的同步原语可能(并且可能不会)跨不同进程工作。 Atomic operations (or types, as C++ has implemented them) do work. 原子操作(或类型,如C ++已实现它们)确实有效。 Memory fences might work too, depending on what you are doing. 内存栅栏可能也可以工作,具体取决于您正在做什么。 IIRC though, Boost's shared memory library provides special synchronization primitives that work across process boundaries (if placed inside the shared area.) 但是,IIRC,Boost的共享内存库提供了跨进程边界工作的特殊同步原语(如果放在共享区域内)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM