共享内存中std::atomic<uint16_t>的使用是否线程/进程安全?
跨进程共享内存中std::atomic<uint16_t>的安全性与疑问解答
我们通过mmap映射同一共享内存区域,在两个进程间传递uint16_t类型数据,依赖std::atomic<I>::is_always_lock_free保证无锁操作,编写了如下代码(运行符合预期),现针对代码中的几个疑问进行解答:
#include <cstdint> #include <atomic> #include <iostream> #include <sys/mman.h> #include <fcntl.h> #include <unistd.h> // sleep #include <time.h> using I = uint16_t; using AI = std::atomic<I>; static_assert(std::atomic<I>::is_always_lock_free); constexpr size_t SIZE = sizeof(AI); constexpr const char *NAME = "nmmm"; constexpr I STOP = 12345; void error(const char *msg){ std::cout << msg << '\n'; exit(10); } int main(int argc, const char **argv){ std::cout << sizeof(I) << ' ' << sizeof(AI) << '\n'; int fd = shm_open(NAME, O_RDWR | O_CREAT, 0644); if (fd < 0) error("shm_open"); int t = ftruncate(fd, SIZE); if (t < 0) error("ftruncate"); void *vmem = mmap(nullptr, SIZE, PROT_WRITE, MAP_SHARED, fd, 0); if (vmem == MAP_FAILED) error("mmap"); std::cout << "All set up!" << ' ' << vmem << '\n'; AI *ai = reinterpret_cast<AI *>(vmem); if (argc > 1){ switch(argv[1][0]){ case 'g': case 'G': ai->store(0, std::memory_order_relaxed); while(true){ auto x = ai->load(std::memory_order_relaxed); std::cout << x << '\n'; if (x == STOP) break; sleep(1); } case 's': case 'S': ai->store(STOP, std::memory_order_relaxed); break; default: { srand(time(nullptr)); I const x = rand() & 0xFFFF; std::cout << "set val to " << x << '\n'; ai->store(x , std::memory_order_relaxed); } break; } } munmap(vmem, SIZE); // shm_unlink(NAME); }
代码的使用方式如下:
# 控制台1:启动读取进程 ./a.out g # 控制台2:设置随机值 ./a.out x # 或者设置停止值,让控制台1的进程退出 ./a.out s
疑问1:未使用placement new,仅通过reinterpret_cast设置初始值是否可行?
可行。因为std::atomic<uint16_t>是POD类型,POD类型的对象不需要构造函数即可初始化其底层存储。通过reinterpret_cast将共享内存的指针转换为std::atomic<uint16_t>*后,直接调用store方法写入值,相当于初始化了该原子对象的底层数据,不存在未定义行为。
疑问2:未调用析构函数是否存在问题?
不存在问题。std::atomic<uint16_t>作为POD类型,其析构函数是平凡的(trivial),调用与否不会改变对象的底层状态,也不会释放任何资源。共享内存的生命周期由shm_unlink和内核管理,因此无需调用析构函数。
疑问3:当满足is_always_lock_free时,sizeof(uint16_t)与sizeof(std::atomic<uint16_t>)是否始终相等?std::atomic是否仅为封装的汇编指令?
- 尺寸方面:当
std::atomic<I>::is_always_lock_free为真时,sizeof(std::atomic<I>)必然等于sizeof(I)。因为无锁原子操作要求原子对象的底层存储与原始类型完全一致,否则无法通过单条硬件指令完成原子读写。 - 实现方面:
std::atomic不仅仅是汇编指令的封装,它还提供了C++内存模型的语义保证,确保不同线程/进程间的内存可见性和操作顺序符合指定的内存序(如代码中的std::memory_order_relaxed)。不过对于无锁的uint16_t原子操作,其核心逻辑确实会编译为单条硬件原子指令,保证操作的原子性。
内容的提问来源于stack exchange,提问作者Nick
相关产品推荐
相关产品推荐

