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

如何用CAS或pthread实现共享内存进程间同步?三类技术疑问求解

共享内存进程间同步的锁实现疑问

我想用如下shmLock结构体控制共享内存的进程间同步:

struct shmLock {
        std::atomic_uint32_t user_count;
        pthread_mutex_t      mutex;
    };

使用逻辑是:当user_count == 0时初始化mutex,再通过CAS操作增加user_count;释放锁时,通过CAS操作减少user_count,当user_count降至0时销毁mutex。我写了对应的myLock类实现:

class myLock {
public:
    myLock(shmLock* lock) : m_lock(lock) {
        uint32_t user_count = m_lock->user_count.load();
        if (user_count == 0) {
            pthread_mutexattr_t mutex_attr{};
            pthread_mutexattr_init(&mutex_attr);
            pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED);
            pthread_mutex_init(&m_lock->mutex, &mutex_attr);
        }
        m_lock->user_count.compare_exchange_strong(user_count, user_count + 1);
        pthread_mutex_lock(&m_lock->mutex);
    }
    ~myLock() {
        pthread_mutex_unlock(&m_lock->mutex);
        uint32_t user_count = m_lock->user_count.load();
        m_lock->user_count.compare_exchange_strong(user_count, user_count - 1);
        if (user_count == 0) {
            pthread_mutex_destroy(&m_lock->mutex);
        }
    }
private:
    shmLock* m_lock;
};

现在有三个技术疑问:

  1. 能否不借助user_count控制mutex的初始化与销毁,直接在构造函数中初始化、析构函数中销毁?
  2. 若compare_exchange_strong执行失败,应如何处理?
  3. 当前实现方式存在不足,是否有更优的共享内存进程间同步控制方案?

问题解答

1. 能否不借助user_count控制mutex的初始化与销毁?

不行。直接在构造函数初始化、析构函数销毁会触发严重的竞态问题:

  • 多个进程同时进入构造函数时,会重复初始化同一个pthread_mutex_t,这属于未定义行为,大概率导致程序崩溃。
  • 析构函数同理,当还有其他进程持有锁时,一个进程销毁mutex会让其他进程在操作锁时触发未定义行为(比如解锁已销毁的mutex)。
    user_count的核心作用就是跟踪当前使用锁的进程/线程数,保证mutex只被初始化一次、销毁一次。

2. compare_exchange_strong执行失败的处理方式

compare_exchange_strong失败意味着:在你读取user_count到执行CAS的这段时间里,其他线程/进程修改了user_count的值。此时必须重新读取最新的user_count,然后重试整个逻辑。

以构造函数为例,正确的循环处理逻辑如下:

myLock(shmLock* lock) : m_lock(lock) {
    uint32_t user_count;
    do {
        user_count = m_lock->user_count.load();
        if (user_count == 0) {
            pthread_mutexattr_t mutex_attr{};
            pthread_mutexattr_init(&mutex_attr);
            pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED);
            // 二次检查user_count,避免其他进程已经完成初始化
            if (m_lock->user_count.load() == 0) {
                pthread_mutex_init(&m_lock->mutex, &mutex_attr);
            }
            pthread_mutexattr_destroy(&mutex_attr); // 别忘了销毁属性对象
        }
    } while (!m_lock->user_count.compare_exchange_strong(user_count, user_count + 1));
    
    pthread_mutex_lock(&m_lock->mutex);
}

析构函数里的CAS失败同理,需要循环重试,且注意判断销毁条件的逻辑错误(原代码判断减之前的user_count == 0是错的,应该判断减之前的值为1,这样减后才是0):

~myLock() {
    pthread_mutex_unlock(&m_lock->mutex);
    uint32_t user_count;
    do {
        user_count = m_lock->user_count.load();
    } while (!m_lock->user_count.compare_exchange_strong(user_count, user_count - 1));
    
    if (user_count == 1) { // 减之前为1,减后为0,此时可以销毁mutex
        pthread_mutex_destroy(&m_lock->mutex);
    }
}

3. 更优的共享内存进程间同步方案

当前实现的核心问题是初始化mutex时的竞态(多个进程同时看到user_count=0,都会尝试初始化),以及CAS循环的效率问题。可以考虑以下几种更可靠的方案:

方案一:用pthread_once_t保证mutex仅初始化一次

把shmLock里的user_count拆分为引用计数和初始化标记,用pthread_once_t跨进程保证初始化的原子性:

struct shmLock {
    std::atomic_uint32_t ref_count;
    pthread_once_t init_flag;
    pthread_mutex_t mutex;
};

// 单独的mutex初始化函数,由pthread_once触发
void init_shm_mutex(void* arg) {
    shmLock* lock = static_cast<shmLock*>(arg);
    pthread_mutexattr_t mutex_attr{};
    pthread_mutexattr_init(&mutex_attr);
    pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED);
    pthread_mutex_init(&lock->mutex, &mutex_attr);
    pthread_mutexattr_destroy(&mutex_attr);
}

class myLock {
public:
    myLock(shmLock* lock) : m_lock(lock) {
        // 保证init_shm_mutex只被执行一次(跨进程安全)
        pthread_once(&m_lock->init_flag, init_shm_mutex, m_lock);
        m_lock->ref_count.fetch_add(1);
        pthread_mutex_lock(&m_lock->mutex);
    }
    ~myLock() {
        pthread_mutex_unlock(&m_lock->mutex);
        // 最后一个使用者销毁mutex
        if (m_lock->ref_count.fetch_sub(1) == 1) {
            pthread_mutex_destroy(&m_lock->mutex);
            // 重置标记,方便后续重新使用(可选)
            pthread_once_init(&m_lock->init_flag, PTHREAD_ONCE_INIT);
        }
    }
private:
    shmLock* m_lock;
};

pthread_once_t本身支持共享内存场景下的跨进程原子执行,能完美解决初始化竞态问题,代码逻辑更简洁可靠。

方案二:直接使用进程间原生同步原语

如果系统支持,可直接用更成熟的跨进程同步工具:

  • POSIX进程间互斥量:提前在共享内存中初始化好带有PTHREAD_PROCESS_SHARED属性的mutex,不需要动态初始化逻辑。
  • POSIX信号量:支持共享内存或文件系统两种方式,API简洁,适合进程间同步。
  • 文件锁(fcntl):不需要共享内存,通过文件描述符实现同步,使用简单但性能略低。

方案三:C标准库跨进程同步(C11+)

如果编译器支持,std::mutex配合共享内存也能实现跨进程同步,但需要手动设置进程共享属性(类似pthread的PTHREAD_PROCESS_SHARED),不同编译器实现可能有差异,稳定性不如直接使用pthread接口。


内容的提问来源于stack exchange,提问作者quanquanquan97

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 23:30:24