程序中止挂起命名的互斥量
Program abort hangs the named mutex
我有几个进程,但当时只有一个应该 运行ning。这意味着假设 Process1 是 运行ning,如果 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";
}
到目前为止,还不错。当这个程序是 运行ning 时,我按 Ctl+C 使程序中止(模拟程序崩溃、未处理的异常等)。之后,当我 运行 应用程序时,程序挂在以下代码行。
named_mutex mutex(open_or_create, "some_name");
boost::lock_guard<named_mutex> guard(mutex) ;
如果我更改互斥量名称,则它可以正常工作而不会被挂起。但是,看起来名为 some_name 的互斥量在机器上不知何故 "remembered" 处于某种不良状态。这会导致任何试图获取名称为 some_name 的互斥锁的应用程序都挂在这行代码上。如果我将此互斥锁名称更改为 some_name2,程序将再次正常工作。
- 谁能解释一下是什么导致了这种行为?
- 如何重置此特定互斥锁的行为?
- 最重要的是,如何在实际应用中避免这种情况?
Can someone please explain what is causing this behavior?
互斥体是全局的。
How can I reset the behavior for this particular mutex?
致电boost::interprocess::named_mutex::remove("mutex_name");
Most importantly, how to avoid this scenario in a real application?
这取决于你的外在问题是什么。也许更明智的解决方案是改用文件锁。当进程被销毁时,文件锁将消失。
更新:
I understand mutex is global but what happens with that mutex that causes the program to hang?
第一个程序获得了互斥量并且从未释放它,所以互斥量仍然存在。互斥量通常在共享状态处于不一致状态时保持,因此自动释放互斥量将是灾难性的。
How can I determine if that mutex_name is in a bad state so its time to call the remove on it?
在你的情况下,你真的做不到,因为你为这项工作选择了错误的工具。你用来判断互斥锁是否处于正常状态的相同逻辑只会解决你的整个问题,所以互斥锁只会让事情变得更难。相反,使用文件锁。将进程名称和进程 ID 写入文件可能有助于故障排除。
如 this answer to the question linked by @ppetraki above, boost::interprocess:named_mutex
, unfortunately, uses a file lock on Windows rather than an actual mutex. If your application terminates abnormally, that file lock will not be removed from the system. This is actually subject to an open issue 中所述。
正在看the source code, we see that, if BOOST_INTERPROCESS_USE_WINDOWS
is defined, internal_mutex_type
maps to a windows_named_mutex
which, internally, uses a windows_named_sync
, which seems to just be using a file lock in the end. I'm not sure what exactly is the rationale of this choice of implementation. Whatever it may be, there does not seem to be any way to get boost::interprocess
to use a proper named mutex on Windows. I would suggest to simply create a named mutex yourself using 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;
}
我有几个进程,但当时只有一个应该 运行ning。这意味着假设 Process1 是 运行ning,如果 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";
}
到目前为止,还不错。当这个程序是 运行ning 时,我按 Ctl+C 使程序中止(模拟程序崩溃、未处理的异常等)。之后,当我 运行 应用程序时,程序挂在以下代码行。
named_mutex mutex(open_or_create, "some_name");
boost::lock_guard<named_mutex> guard(mutex) ;
如果我更改互斥量名称,则它可以正常工作而不会被挂起。但是,看起来名为 some_name 的互斥量在机器上不知何故 "remembered" 处于某种不良状态。这会导致任何试图获取名称为 some_name 的互斥锁的应用程序都挂在这行代码上。如果我将此互斥锁名称更改为 some_name2,程序将再次正常工作。
- 谁能解释一下是什么导致了这种行为?
- 如何重置此特定互斥锁的行为?
- 最重要的是,如何在实际应用中避免这种情况?
Can someone please explain what is causing this behavior?
互斥体是全局的。
How can I reset the behavior for this particular mutex?
致电boost::interprocess::named_mutex::remove("mutex_name");
Most importantly, how to avoid this scenario in a real application?
这取决于你的外在问题是什么。也许更明智的解决方案是改用文件锁。当进程被销毁时,文件锁将消失。
更新:
I understand mutex is global but what happens with that mutex that causes the program to hang?
第一个程序获得了互斥量并且从未释放它,所以互斥量仍然存在。互斥量通常在共享状态处于不一致状态时保持,因此自动释放互斥量将是灾难性的。
How can I determine if that mutex_name is in a bad state so its time to call the remove on it?
在你的情况下,你真的做不到,因为你为这项工作选择了错误的工具。你用来判断互斥锁是否处于正常状态的相同逻辑只会解决你的整个问题,所以互斥锁只会让事情变得更难。相反,使用文件锁。将进程名称和进程 ID 写入文件可能有助于故障排除。
如 this answer to the question linked by @ppetraki above, boost::interprocess:named_mutex
, unfortunately, uses a file lock on Windows rather than an actual mutex. If your application terminates abnormally, that file lock will not be removed from the system. This is actually subject to an open issue 中所述。
正在看the source code, we see that, if BOOST_INTERPROCESS_USE_WINDOWS
is defined, internal_mutex_type
maps to a windows_named_mutex
which, internally, uses a windows_named_sync
, which seems to just be using a file lock in the end. I'm not sure what exactly is the rationale of this choice of implementation. Whatever it may be, there does not seem to be any way to get boost::interprocess
to use a proper named mutex on Windows. I would suggest to simply create a named mutex yourself using 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;
}