c++ 如何编写单进程named_mutex?

Posted

技术标签:

【中文标题】c++ 如何编写单进程named_mutex?【英文标题】:c++ how can I write single-process named_mutex? 【发布时间】:2019-12-17 12:28:50 【问题描述】:

我需要一个允许我锁定/解锁特定名称(或只是索引)的类,并且我不希望它是多处理的,因此我可以运行我的应用程序的多个实例。此外,我想避免使用特定于系统的 API,只是 std 或 boost。 (为简单起见,我们可以说:同时使用的名称/索引的最大数量为 100)

不幸的是,我没有给你使用示例,我只是感兴趣是否可以制作。

我试图找到类似的东西,但我找到的只是boost::interprocess::named_mutex 和一些WinApi 方法,比如CreateMutexW

我也尝试编写自己的代码(如下),但它绝对不完美,并且至少有一个潜在的错误。

那么,有没有人有任何建议、代码想法或已经存在的类?

提前致谢

class IndexMutex

public:
    void Lock(uint32_t id);
    void Unlock(uint32_t id);

private:
    struct IndexLock
    
        static constexpr uint32_t unlocked = ~0u;
        void Lock(uint32_t id) 
            index_ = id;
            mutex_.lock();
        
        void Unlock() 
            mutex_.unlock();
            index_ = unlocked;
        
        bool IsLocked() const 
            return index_ != unlocked;
        

        std::atomic<uint32_t> index_ = unlocked;
        std::mutex mutex_;
    ;

    std::array<IndexLock, 100> mutexes_;
    std::mutex masterMutex_;
;

void IndexMutex::Lock(uint32_t id)

    if (id == IndexLock::unlocked) 
        return;
    

    const std::lock_guard<std::mutex> __guard masterMutex_ ;

    uint32_t possibleId = IndexLock::unlocked;

    for (uint32_t i = 0; i < mutexes_.size(); ++i) 
        if (mutexes_[i].index_ == id) 
            masterMutex_.unlock();

            // POTENTIAL BUG: TIME GAP

            mutexes_[i].Lock(id);

            return;
        
        // Searching for unlocked mutex in the same time.
        if (possibleId == IndexLock::unlocked && !mutexes_[i].IsLocked()) 
            possibleId = i;
        
    

    if (possibleId == IndexLock::unlocked) 
        throw std::runtime_error "No locks were found." ;
    

    // We are sure here, that mutex can't be locked
    // because we were protected by the muster mutex all that time.
    mutexes_[possibleId].Lock(id);


void IndexMutex::Unlock(uint32_t id)

    if (id == IndexLock::unlocked) 
        return;
    

    const std::lock_guard<std::mutex> __guard masterMutex_ ;

    for (auto& lock : mutexes_) 
        if (lock.index_ == id) 
            lock.Unlock();
            return;
        
    

    throw std::runtime_error "No mutexes there found by specified index." ;

【问题讨论】:

目前还不太清楚为什么需要所有这些机器。为什么一个普通的 std::array 不适合你? 你这是什么意思?如果您建议为每个可能的索引使用单独的互斥锁,这将是很大的内存开销(我们最多可以有 4294967294 个不同的索引,以及更多的名称)。如果你的意思是为了简单起见从我的代码中删除musterMutex_(只具有可以接受索引的互斥体的数组),那么当两个线程可以锁定相同的索引时我们会遇到问题.. 好吧,看起来你想要一个(线程安全的)map&lt;int, mutex&gt; 之类的东西。 在某种程度上是的,但是如何使 map 线程安全,而不会在互斥部分上死锁.. 为什么会出现死锁?应该没有。只需要一个主互斥锁来保护地图。 【参考方案1】:

您想要一个引用计数互斥映射,受主互斥锁保护。

方面的实现
std::map<int, std::pair<int, std::mutex>>

会做这项工作。

锁操作是这样工作的(未经测试的伪代码):

master.lock()
std::pair<int, std::mutex>& m = mymap[index]; //inserts a new one if needed       
m.first++;
master.unlock();
m.second.lock();

解锁操作:

master.lock();
std::pair<int, std::mutex>& m = mymap[index];
m.second.unlock();
m.first--;
if (m.first==0) mymap.remove(index);
master.unlock();

没有死锁!可以先解锁主控,然后锁定找到的互斥锁。即使另一个线程干预并解锁互斥体,引用计数也不会降至零,并且互斥体也不会被删除。

【讨论】:

非常感谢)我想这是我寻找的解决方案) 我唯一建议的是在解锁操作中使用map::find 而不是operator[],所以我们确定索引实际上在地图中。如果万一我们得到不正确的索引,则解锁后该互斥锁的计数将变为-1。 这只是一个粗略的轮廓,根据需要调整。

以上是关于c++ 如何编写单进程named_mutex?的主要内容,如果未能解决你的问题,请参考以下文章

boost::named_mutex: 最后一个进程关闭时安全清理

如何使用 C++ 编写具有多级指针的进程内存?

怎么用c++编写一个Windows服务程序来监控另一个程序,崩溃后重新启动。

用于 Windows 的 C++ 互斥锁

提升 named_mutex 和 remove() 命令

boost::interprocess::named_mutex 是不是需要存储在共享内存中?