程序中止挂起命名的互斥锁

问题描述 投票:0回答:2

我有几个进程,但当时只有一个进程。这意味着让我们说Process1正在运行,如果Process2启动,那么Process2应该等到Process1完成。我正在考虑为此目的提升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
2个回答
2
投票

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

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

#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;
}

1
投票

有人可以解释导致这种行为的原因吗?

互斥体是全球性的。

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

打电话给boost::interprocess::named_mutex::remove("mutex_name");

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

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

更新:

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

第一个程序获得了互斥锁,但从未发布它,因此互斥锁仍然存在。当共享状态处于不一致状态时,通常会保留互斥锁,因此自动释放互斥锁将是灾难性的。

如何确定mutex_name是否处于错误状态,以便有时间调用它上面的删除?

在你的情况下,你真的不能,因为你选择了错误的工具。你用来判断互斥锁是否处于理智状态的相同逻辑只会解决你的整个问题,所以互斥锁只会让事情变得更难。而是使用文件锁。将进程名称和进程ID写入文件以帮助进行故障排除可能很有用。

© www.soinside.com 2019 - 2024. All rights reserved.