You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

共享内存中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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 20:06:22