我正在开发一个应该只运行一个实例的应用。我刚刚添加了一个表示"单实例获取"的类,如果可执行文件被视为已经运行,它会将命令行参数传递给侦听方法另一边。它使用Win32互斥锁以及boost message_queue。
InstanceManager::InstanceManager()
{
LPCSTR versionCstr = "MyApp";
myMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, versionCstr);
if (!myMutex && GetLastError() == ERROR_FILE_NOT_FOUND)
{
// This mutex did not already exist, so we are the first to the plate. Create mutex, message queue
myMutex = CreateMutex(0, TRUE, versionCstr);
message_queue::remove("MyApp");
messageQueue.reset(new message_queue(create_only, "MyApp", 5, 512));
receiveThread.reset(new boost::thread(bind(&InstanceManager::receiveThread_Internal, this)));
}
else if (!myMutex)
{
// Could not create mutex, but can't tell why
throw exception("Unexpected error creating mutex - " + GetLastError());
}
else
{
// Mutex already existed. We are the second instance of this program. (Don't do anything just
// yet - send method is outside)
messageQueue.reset(new message_queue(open_only, "MyApp"));
}
}
似乎大约有5次中的3次,这是正常的。如果这是第一个exe实例,它进入第一个if块。如果这是第二个,它进入else块。但是,有时它会到达message_queue的构造函数并遇到interprocess_exception,并带有详细信息:"系统找不到指定的文件。"此时,还有另一个应用实例正在运行,它应该正在侦听消息(这是receiveThread
所做的事情。)
鉴于我甚至无法说明为什么message_queue会处理文件(我真的希望它不会写入磁盘来简化"简化")我真的不是确定用这个来看看。
我的代码还有更多内容,但是它没有达到它,所以我觉得这不可能是相关的。
答案 0 :(得分:0)
The problem is that InstanceManager
constructor and if statement can be entered by more than one application at the same time if mutex file is not created yet.
You can lock access to InstanceManager
per one application using boost's named_mutex
. Also message queue would need to be removed by last process (you will need to track somehow which process is last one quitting, for example by using shared memory).
Better implementation could look like this:
InstanceManager::InstanceManager()
{
using boost::interprocess;
named_mutex mutex(open_or_create, "my_named_mutex");
scoped_lock<named_mutex> lock(mutex);
try
{
messageQueue.reset(new message_queue(open_only, "MyApp"));
return;
}
catch(interprocess_exception &ex)
{
// OK, this is the first instance so we need to create the message queue first
}
messageQueue.reset(new message_queue(create_only, "MyApp", 5, 512));
receiveThread.reset(new boost::thread(bind(&InstanceManager::receiveThread_Internal, this)));
}
InstanceManager::~InstanceManager()
{
// last process removes the queue
message_queue::remove("MyApp");
named_mutex::remove("my_named_mutex");
}