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



我有几个进程,但当时只有一个进程应该运行。这意味着假设进程 1 正在运行,如果进程 2 启动,则进程2应等到进程 1完成。我正在考虑为此目的进行提升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. 最重要的是,如何在实际应用程序中避免这种情况?

正如对上面@ppetraki链接的问题的回答中所解释的那样,不幸的是,boost::interprocess:named_mutex在Windows上使用文件锁而不是实际的互斥锁。如果应用程序异常终止,则不会从系统中删除该文件锁定。这实际上受到一个悬而未决的问题的影响。

查看源代码,我们看到,如果定义了BOOST_INTERPROCESS_USE_WINDOWSinternal_mutex_type映射到一个windows_named_mutex,该内部使用windows_named_sync,它似乎只是在最后使用文件锁。我不确定这种实现选择的确切理由是什么。不管是什么,似乎没有任何方法可以让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 wrongn";
return -1;
}
return 0;
}

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

互斥锁是全局的。

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

呼叫boost::interprocess::named_mutex::remove("mutex_name");

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

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

更新:

我知道互斥锁

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

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

如何确定该mutex_name是否处于错误状态,以便是时候对其进行删除了?

就你而言,你真的不能,因为你为工作选择了错误的工具。你用来判断互斥锁是否处于正常状态的逻辑只会解决你的整个问题,所以互斥锁只会让事情变得更加困难。请改用文件锁定。将进程名称和进程 ID 写入文件以帮助进行故障排除可能很有用。

最新更新