c++11 进程间原子和互斥锁

Posted

技术标签:

【中文标题】c++11 进程间原子和互斥锁【英文标题】:c++11 interprocess atomics and mutexes 【发布时间】:2012-10-31 15:15:26 【问题描述】:

我有一个 Linux 程序,它产生多个进程(fork)并通过 POSIX 共享内存进行通信。我想让每个进程分配一个 id (0-255)。我的意图是在共享内存区域(初始化为零)中放置一个位向量,然后原子地比较和交换位以分配一个 id。

有没有对 c++11 友好的方法来做到这一点?我可以创建一个原子位集吗?我可以跨进程使用互斥锁吗?如何确保构造函数在所有进程中被调用一次且仅调用一次?

【问题讨论】:

@aleguna 因为我想要一个 0-255 之间的值,当一个进程离开这个程序时,它应该释放它的 ID 以供重用。 @NicolBolas 我提出了我的问题并选择了最少的必要信息来提出一个可以让我解决问题的问题。我不是在寻找人们来改变问题所在。我可以创建一个任意同步问题,并就跨进程的 posix 共享内存询问它,问题仍然存在。如果对您有帮助,请假装我询问了共享数据结构以及 c++ 互斥体或原子如何在这方面发挥作用。 @dschatz:因此,你有一个classic XY question. @NicolBolas 根据您的第一个问题,您显然错过了重点。我一般在问 c++11 互斥体和原子如何跨进程工作(嘿,标题匹配!)。我举一个具体的例子来具体说明。然后,您继续对示例进行挑剔。如果你没有答案,那很好,但没有理由试图移动球门柱来回答我的问题。 C++11 没有进程间函数。 Boost::Interprocess 可能为您提供了一些工具来解决这个问题。 【参考方案1】:

C++11 线程原语(互斥体、原子等)是线程原语。 C++ 标准不引用进程,并且这些工具中的大多数不跨进程互操作。

标准中唯一提到的流程是在一个非规范符号中表示lock-free atomics are intended to be OK for IPC:

无锁的操作也应该是无地址的。也就是说,通过两个不同地址对同一内存位置的原子操作将以原子方式进行通信。实现不应依赖于任何每个进程的状态。此限制允许通过多次映射到一个进程的内存和两个进程之间共享的内存进行通信。

在这种非规范性表示法之外,线程原语并非旨在成为实现进程间通信的一种手段。当这些对象放置在共享内存中时(除了上面提到的无锁原子),这些对象的行为是未定义的。

【讨论】:

[atomics.lockfree] 包括“[注意:无锁的操作也应该是无地址的。也就是说,通过两个不同地址对同一内存位置的原子操作将进行原子通信。实现不应依赖于任何每个进程的状态。此限制允许通过多次映射到进程的内存以及在两个进程之间共享的内存进行通信。- end note ]" 亲爱的@Nicol Bolas,你的回答很好,但我找不到任何地方或任何关于此的书籍,所以我仍然对在共享内存中使用 C++11 互斥锁感到困惑。 (据我所知,如果使用 PTHREAD_PROCESS_SHARED,pthread 互斥锁是可以的) 我真的希望这包含在 en.cppreference.com/w/cpp/thread/mutex ? 等参考资料中 这个答案是完全错误的。 Jefrrey Yasskin 在他的评论中已经指出了一个原因(std::atomic 实际上非常适合共享内存 IPC,有时是必要的),但他的评论很容易被掩盖。互斥体也可以用于共享内存 IPC,尽管标准库互斥体确实不适合这种用途。 并且,为了清楚起见,该标准的意图显然是原子应该可用于 IPC。 Jeffrey 在标准中的引用表明这是真的。也就是说,我不知道这是否已经在 C++11 的标准中。【参考方案2】:

你可以在共​​享内存块内部使用互斥锁,但是必须将互斥锁声明为SHARED,因此在共享内存块内部使用互斥锁并不罕见,你可以自己创建类,很简单:

class Mutex 
private:
    void *_handle;
public:
    Mutex(void *shmMemMutex,  bool recursive =false, );
    virtual ~Mutex();

    void lock();
    void unlock();
    bool tryLock();
;

Mutex::Mutex(void *shmMemMutex, bool recursive)

    _handle = shmMemMutex;
    pthread_mutexattr_t attr;
    ::pthread_mutexattr_init(&attr);
    ::pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
    ::pthread_mutexattr_settype(&attr, recursive ? PTHREAD_MUTEX_RECURSIVE_NP : PTHREAD_MUTEX_FAST_NP);

    if (::pthread_mutex_init((pthread_mutex_t*)_handle, &attr) == -1) 
        ::free(_handle);
        throw ThreadException("Unable to create mutex");
    

Mutex::~Mutex()

    ::pthread_mutex_destroy((pthread_mutex_t*)_handle);

void Mutex::lock()

    if (::pthread_mutex_lock((pthread_mutex_t*)_handle) != 0) 
        throw ThreadException("Unable to lock mutex");
    

void Mutex::unlock()

    if (::pthread_mutex_unlock((pthread_mutex_t*)_handle) != 0) 
        throw ThreadException("Unable to unlock mutex");
    

bool Mutex::tryLock()

    int tryResult = ::pthread_mutex_trylock((pthread_mutex_t*)_handle);
    if (tryResult != 0) 
        if (EBUSY == tryResult) return false;
        throw ThreadException("Unable to lock mutex");
    
    return true;

【讨论】:

以上是关于c++11 进程间原子和互斥锁的主要内容,如果未能解决你的问题,请参考以下文章

互斥锁,自旋锁,原子操作原理和实现

使用互斥锁进行进程间同步 - 获取 AME

线程间的通信方式以及线程与进程的区别

临界区(critical section 每个线程中访问 临界资源 的那段代码)和互斥锁(mutex)的区别(进程间互斥量共享内存虚拟地址)

C++线程间的互斥和通信

C++线程间的互斥和通信