3

我正在为 Windows 开发一个 C++ 项目,需要一个好的互斥锁实现来同步黑白进程(即 winapi 的包装)。我目前正在使用 boost::interprocess::named_mutex,但是我注意到如果其中一个进程崩溃 - 互斥锁将永远保持锁定状态(即使在重新启动后!)。谷歌搜索这件事证实了这实际上是行为。

坦率地说,我看不出这种行为在任何项目中是如何被接受的。错误存在,进程崩溃 - 这只是意味着一个小错误可能导致系统永远无法再次工作......不可接受!

所以我的问题是:

  1. 任何人都可以推荐具有“正常”行为的不同实现(即互斥锁在崩溃时被放弃,并且在系统重新启动后将“重置”以正常工作)?跨平台显然更好,但绝对不是必须的。

  2. 出于好奇- boost 怎么可能让这种行为发生?

谢谢!

4

3 回答 3

4

boost::interprocess的文档解释了不同类的持久性。在 named_mutex 的情况下,它被列为内核或文件系统。由此我们可以假设 Windows 实现是使用文件系统,这就是它在您重新启动系统后仍然存在的原因。

根据您的情况,您可以为每个进程将自己的文件写入磁盘,然后在进程安全关闭时将其删除。如果 fie 存在,则当进程重新启动时,一定是不干净的关闭,您可以使用boost::interprocess::named_mutex::remove静态函数来删除互斥锁。

于 2012-10-16T08:04:05.130 回答
0

看看 StlSoft 的process_mutex。跨平台(使用platformstl 组件,而不是操作系统特定的winstl 或unixstl 版本)和头文件。

于 2012-10-16T08:12:27.047 回答
0

我为此推出了自己的互斥锁,因为我不想仅仅为了这个功能而向某个重量级库添加依赖项。它用于生产代码并包含在我们的单元测试中 - 如果您发现任何问题,请告诉我。

这是一个仅限 Windows 的解决方案。

使用示例:

NamedSystemMutex m{ L"MyMutex" };
std::lock_guard<NamedSystemMutex> lock{ m };

头文件名为SystemMutex.h:

#pragma once

#include <string>
#include <windows.h> // Just needed for HANDLE

// Provides a system-wide, recursive, named lock. 
// This class satisfies requirements of C++11 concept "Lockable",  i.e. can be (and should be) used with unique_lock etc.
class NamedSystemMutex
{
public:
    explicit NamedSystemMutex(const std::wstring& name);
    ~NamedSystemMutex();

    // Moveable, not copyable
    NamedSystemMutex(const NamedSystemMutex& other) = delete;
    NamedSystemMutex(NamedSystemMutex&& other) = default;
    NamedSystemMutex& operator=(const NamedSystemMutex& other) = delete;
    NamedSystemMutex& operator=(NamedSystemMutex&& other) = default;

    void lock();
    void unlock();
    bool try_lock();

private:
    HANDLE handle_{};
};

执行:

#include "NamedSystemMutex.h"
#include <stdexcept>

NamedSystemMutex::NamedSystemMutex(const std::wstring& name) {
    handle_ = CreateMutexW(nullptr, FALSE, name.c_str());
    if (handle_ == NULL) {
        throw std::runtime_error("Creation of mutex failed");
    }
}

NamedSystemMutex::~NamedSystemMutex() {
    const BOOL result = CloseHandle(handle_);
    if (result == FALSE) {
        // Error: Failed to close mutex handle (Error ignored since we are in destructor)
    }
}

void NamedSystemMutex::lock() {
    const auto result = WaitForSingleObject(handle_, INFINITE);
    if (result == WAIT_ABANDONED) {
        // Warning: Lock obtained, but on an abandoned mutex (was not correctly released before, e.g. due to a crash)
    }
    else if (result != WAIT_OBJECT_0) {
        throw std::runtime_error("Failed to acquire lock");
    }
}

void NamedSystemMutex::unlock() {
    const BOOL result = ReleaseMutex(handle_);
    if (result == FALSE) {
        throw std::runtime_error("Failed to release lock: calling thread does not own the mutex");
    }
}

bool NamedSystemMutex::try_lock() {
    const auto result = WaitForSingleObject(handle_, 0);
    if (result == WAIT_TIMEOUT) {
        return false;
    }
    if (result == WAIT_OBJECT_0) {
        return true;
    }
    throw std::runtime_error("Failed to acquire lock");
}
于 2020-12-03T13:03:29.380 回答