【问题标题】:Interprocess reader/writer lock with Boost使用 Boost 的进程间读写器锁
【发布时间】:2012-09-08 11:30:13
【问题描述】:

This thread 在解释如何使用 Boost 实现读取器/写入器锁时非常出色。它看起来相对简单,我真的很喜欢它,但它似乎也使用了非命名锁,我需要一个进程间解决方案(不需要可移植,可以仅限 Windows)。

有没有办法拥有一个进程间shared_mutex?我看到有一个 named_mutex,但我无法让它与 shared_lock 其他锁一起使用。

感谢任何指针。

[编辑]

与此同时,我遇到了this thread,它几乎一针见血。我有两个问题:

  1. 它没有显示完整的代码(我猜我需要使用named_upgradable_mutex,但我不太确定)和
  2. 我不喜欢修改后的“编写器”的答案,它不使用在析构函数中解锁的现成类,而是对互斥锁进行 3 次原始调用的序列。

仍然欢迎评论或好的解决方案。

【问题讨论】:

  • 请注意,即使使用 scoped_lock 或 shared_lock 也不能保护您在程序崩溃时不让进程间互斥锁处于锁定状态。我发现我需要编写一个实用程序来调用 boost::interprocess::named_upgradable_mutex::remove(),以便在发生此类事件后清理互斥锁。在 linux 上,这似乎调用了 shm_unlink(),尽管我不确定这就是它所做的全部。顺便说一句,您可以在 /dev/shm/ 中看到您的命名锁。

标签: c++ boost boost-interprocess


【解决方案1】:

Boost.Interprocess 文档描述了它支持的所谓的upgradable mutexes 和两种支持的可升级互斥锁类型的upgradable mutex operations

编辑:我相信这可行:

#include <iostream>
#include <string>
#include <unistd.h>

#include <boost/scope_exit.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/sync/interprocess_upgradable_mutex.hpp>
#include <boost/interprocess/sync/scoped_lock.hpp>
#include <boost/interprocess/sync/sharable_lock.hpp>
#include <boost/interprocess/sync/upgradable_lock.hpp>

// http://stackoverflow.com/questions/12439099/interprocess-reader-writer-lock-with-boost/

#define SHARED_MEMORY_NAME "SO12439099-MySharedMemory"

struct shared_data {
private:
    typedef boost::interprocess::interprocess_upgradable_mutex upgradable_mutex_type;

    mutable upgradable_mutex_type mutex;
    volatile int counter;

public:
    shared_data()
        : counter(0)
    {
    }

    int count() const {
        boost::interprocess::sharable_lock<upgradable_mutex_type> lock(mutex);
        return counter;
    }

    void set_counter(int counter) {
        boost::interprocess::scoped_lock<upgradable_mutex_type> lock(mutex);
        this->counter = counter;
    }
};

int main(int argc, char *argv[])
{
    using namespace boost::interprocess;

    if (argc != 2) {
        std::cerr << "Usage: " << argv[0] << " WHICH" << std::endl;
        return 1;
    }

    const std::string which = argv[1];
    if (which == "parent") {
        shared_memory_object::remove(SHARED_MEMORY_NAME);
        shared_memory_object shm(create_only, SHARED_MEMORY_NAME, read_write);

        BOOST_SCOPE_EXIT(argc) {
            shared_memory_object::remove(SHARED_MEMORY_NAME);
        } BOOST_SCOPE_EXIT_END;

        shm.truncate(sizeof (shared_data));

        // Map the whole shared memory into this process.
        mapped_region region(shm, read_write);

        // Construct the shared_data.
        new (region.get_address()) shared_data;

        // Go to sleep for a minute.
        sleep(60);

        return 0;
    } else if (which == "reader_child") {
        shared_memory_object shm(open_only, SHARED_MEMORY_NAME, read_write);

        mapped_region region(shm, read_write);
        shared_data& d = *static_cast<shared_data *>(region.get_address());

        for (int i = 0; i < 100000; ++i) {
            std::cout << "reader_child: " << d.count() << std::endl;
        }
    } else if (which == "writer_child") {
        shared_memory_object shm(open_only, SHARED_MEMORY_NAME, read_write);

        mapped_region region(shm, read_write);
        shared_data& d = *static_cast<shared_data *>(region.get_address());

        for (int i = 0; i < 100000; ++i) {
            d.set_counter(i);
            std::cout << "writer_child: " << i << std::endl;
        }
    }
}

我在 Mac 上使用以下脚本进行了尝试:

#!/usr/bin/env sh
./a.out reader_child &
./a.out reader_child &
./a.out writer_child &
./a.out reader_child &
./a.out reader_child &

(必须先启动父级:./a.out parent

输出显示“reader_child”和“writer_child”行交错(所有“reader_child”行在第一个“writer_child”行之后显示非零值),因此它似乎可以正常工作。

【讨论】:

  • 谢谢!即使它不完全是我要找的东西,对我来说也能解决一些疑问。
  • 当计数器被锁保护时,为什么要使用 volatile?
  • @BryanFok:我使用了volatile,因为我在锁定interprocess_upgradable_mutex 时没有看到任何内存屏障保证。共有三种实现方式:posix、windows 和 spin。如果使用 posix 或 windows 实现,我认为不需要 volatile,因为 pthread_mutex_lock() / WaitForSingleObject() 是在幕后调用的。自旋实现使用原子 CAS 操作,我认为有些架构中 CAS 没有内存屏障保证。例如,请参阅this answer
猜你喜欢
  • 1970-01-01
  • 2020-09-13
  • 2011-05-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-24
相关资源
最近更新 更多