【问题标题】:Program abort hangs the named mutex程序中止挂起命名的互斥锁
【发布时间】:2019-09-13 05:17:09
【问题描述】:

我有几个进程,但当时只有一个应该在运行。这意味着假设 Process1 正在运行,如果 Process2 启动,那么 Process2 应该等到 Process1做完了。为此,我正在考虑 boost named_mutex。为了避免在抛出某些异常时互斥锁可能不会被释放的情况,看起来 boost::lock_guard 可能很有用。我想出了以下代码的简化版本。

#include <iostream>
#include <boost/interprocess/sync/named_mutex.hpp>
#include <boost/thread.hpp>
#include <chrono>
#include <thread>

using namespace boost::interprocess;
#pragma warning(disable: 4996)
int main()
{


    std::cout << "Before taking lock" << std::endl;

    named_mutex mutex(open_or_create, "some_name");
    boost::lock_guard<named_mutex> guard(mutex) ;

    // Some work that is simulated by sleep
    std::cout << "now wait for 10 second" << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(10));

    std::cout << "Hello World";


}

到目前为止,一切都很好。当这个程序运行时,我点击了 Ctl+C,所以程序被中止(模拟程序崩溃、未处理的异常等)。之后,当我运行应用程序时,程序会挂在下面的代码行中。

named_mutex mutex(open_or_create, "some_name");
boost::lock_guard<named_mutex> guard(mutex) ;

如果我更改互斥锁名称,那么它可以正常工作而不会挂起。但是,看起来名为 some_name 的互斥体在某种不良状态下以某种方式在机器上“记住”了。这会导致任何尝试获取名为 some_name 的互斥锁的应用程序都会挂在这行代码上。如果我将此互斥体名称更改为 some_name2,程序将再次正常工作。

  1. 谁能解释一下导致这种行为的原因?
  2. 如何重置此特定互斥体的行为?
  3. 最重要的是,如何在实际应用中避免这种情况?

【问题讨论】:

标签: c++ windows visual-studio boost mutex


【解决方案1】:

正如this answerthe question linked by @ppetraki above 中所述,不幸的是,boost::interprocess:named_mutex 在 Windows 上使用文件锁而不是实际的互斥锁。如果您的应用程序异常终止,该文件锁定将不会从系统中删除。这其实是主题to an open issue

查看the source code,我们看到,如果定义了BOOST_INTERPROCESS_USE_WINDOWSinternal_mutex_type 映射到windows_named_mutex,在内部,uses a windows_named_sync,最后是seems to just be using a file lock。我不确定这种实施选择的确切理由是什么。不管它是什么,似乎没有任何方法可以让boost::interprocess 在 Windows 上使用正确命名的互斥锁。我建议自己使用CreateMutex 创建一个命名互斥锁,例如:

#include <type_traits>
#include <memory>
#include <stdexcept>
#include <mutex>
#include <iostream>

#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

struct CloseHandleDeleter { void operator ()(HANDLE h) const { CloseHandle(h); } };

class NamedMutex
{
    std::unique_ptr<std::remove_pointer_t<HANDLE>, CloseHandleDeleter> m;

public:
    NamedMutex(const wchar_t* name)
        : m(CreateMutexW(nullptr, FALSE, name))
    {
        if (!m)
            throw std::runtime_error("failed to create mutex");
    }

    void lock()
    {
        if (WaitForSingleObject(m.get(), INFINITE) == WAIT_FAILED)
            throw std::runtime_error("something bad happened");
    }

    void unlock()
    {
        ReleaseMutex(m.get());
    }
};

int main()
{
    try
    {
        NamedMutex mutex(L"blub");

        std::lock_guard lock(mutex);

        std::cout << "Hello, World!" << std::endl;
    }
    catch (...)
    {
        std::cerr << "something went wrong\n";
        return -1;
    }

    return 0;
}

【讨论】:

  • 非常好!!!。在您使用“Global\\blub”的名称中,“Global\\::”有什么意义吗?或者这个名称实际上可能是“blah-blah”之类的东西??
  • @BKS Global\\ 前缀会将互斥锁添加到全局namespace。很可能,您的应用程序实际上并不需要它……
  • @BKS,就在 NT 系统中的实现而言(都是从 XP 开始),Windows 在对象管理器目录“\Sessions\\BaseNamedObjects”中创建非设备命名的内核对象。此目录中有一个指向全局“\BaseNamedObjects”目录的“全局”符号链接。这个全局命名对象目录也用于会话 0,即服务会话。由于这确实是一条 NT 路径,我们必须使用反冲,即"Global\\blub"。我们不能使用"Global/blub",因为正斜杠只是 NT 路径中的一个名称字符,除了在文件系统中保留。
【解决方案2】:

有人能解释一下是什么导致了这种行为吗?

互斥锁是全局的。

如何重置此特定互斥体的行为?

致电boost::interprocess::named_mutex::remove("mutex_name");

最重要的是,如何在实际应用中避免这种情况?

这取决于你的外部问题是什么。也许更明智的解决方案是使用文件锁。当进程被销毁时,文件锁将消失。

更新:

我知道互斥锁是全局的,但是导致程序挂起的互斥锁会发生什么?

第一个程序获取了互斥体并且从未释放它,所以互斥体仍然被持有。互斥锁通常在共享状态进入不一致状态时被持有,因此自动释放互斥锁将是灾难性的。

我如何确定 mutex_name 是否处于错误状态,以便对其调用 remove?

在您的情况下,您确实不能,因为您选择了错误的工具来完成这项工作。您用来判断互斥锁是否处于正常状态的相同逻辑只会解决您的整个问题,因此互斥锁只会让事情变得更难。相反,使用文件锁。将进程名称和进程 ID 写入文件可能有助于排除故障。

【讨论】:

  • 我知道互斥锁是全局的,但是导致程序挂起的互斥锁会发生什么?
  • 我如何确定 mutex_name 是否处于错误状态,以便对其调用 remove?
  • 我只是想避免应用程序套件中的许多程序同时运行的情况。如果其中一个程序正在运行,我希望其他程序等到第一个程序完成。
  • here
  • @BKS 您的印象不正确。 lock_guard 在其析构函数运行时释放锁。如果析构函数没有运行,锁就不会被释放。例如,如果你用new 分配一个lock_guard 而没有delete 它,锁将不会被释放。在您的情况下,析构函数不会被调用,因为进程在没有破坏 lock_guard 的情况下终止。
猜你喜欢
  • 2016-05-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-06
  • 2023-03-09
  • 1970-01-01
  • 2017-09-24
  • 2021-06-21
相关资源
最近更新 更多