CreateMutex never returns ERROR_ALREADY_EXISTS
我正在使用CreateMutex停止多个应用程序以同时运行某些功能。这些函数在dll中,因此可以由同一应用程序或单独的应用程序调用。该dll与硬件通信,因此如果其他功能已在运行,则我想返回"繁忙",而不必等待。我认为最好的方法是使用CreateMutex而不是OpenMutex的组合。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| int FunctionExposedByDll()
{
hMutexAPI = CreateMutex(0, 0, API_RUNNING_MUTEXT );
if (!hMutexAPI )
{
DWORD dwErr = GetLastError();
if (dwErr == ERROR_ALREADY_EXISTS )
return MY_ERROR_BUSY;
}
// actual function here
CloseHandle( hMutexAPI );
} |
因此,如果已经创建了互斥锁,则应该获取ERROR_ALREADY_EXISTS,它告诉我系统正在执行api,并且应该返回。但是,即使先前的函数未返回且互斥锁句柄未关闭,上述代码也始终返回有效的互斥锁。
我还尝试了CreateMutexEx函数,在这种情况下,当我第二次尝试使用它时,当我期望ERROR_ALREADY_EXISTS时它返回ERROR_ACCESS_DENIED。所以我的问题是,要获得互斥体已经存在的正确状态,我需要怎么做?
我正在使用Windows 7
**更新**
基于Rob K的答案,我将代码更改为以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| int FunctionExposedByDll()
{
hMutexAPI = CreateMutex(0, 0, API_RUNNING_MUTEXT );
DWORD dwErr = GetLastError();
if (dwErr == ERROR_ALREADY_EXISTS )
{
CloseHandle( hMutexAPI); // i have to call this but it contributes to first chance exception too!
return MY_ERROR_BUSY;
}
// actual function here
CloseHandle( hMutexAPI );
} |
现在,我正在获取/读取信号量的正确状态,但是释放是一个问题。如果我在处于"繁忙状态"时不释放它,那么即使其他API已完成,我也总是会收到ERROR_ALREADY_EXISTS。因此,CloseHandle()修复了该问题,但又产生了另一个问题。当我从繁忙状态返回并关闭CloseHandle()时,第一个API稍后完成并想要关闭句柄时,我得到了第一次机会异常。我看不出该如何避免!!
这不是使用CreateMutex()的正确方法。 CreateMutex()(几乎)总是成功地将有效的句柄返回给互斥锁。从链接的MSDN文档中:"如果互斥锁是命名的互斥锁,并且该函数调用之前存在该对象,则返回值是现有对象的句柄,GetLastError返回ERROR_ALREADY_EXISTS ..."
再次引用:"两个或多个进程可以调用CreateMutex来创建相同的命名互斥锁。第一个进程实际上创建了互斥锁,具有足够访问权限的后续进程只是打开了现有互斥锁的句柄。这使多个进程可以获取相同的互斥量,同时使用户不必承担确保首先启动创建过程的责任。"
您要执行的操作是使用CreateMutex()打开互斥锁的句柄,然后使用超时值为0的WaitForSingleObject()尝试使用它。如果WaitForSingleObject()无法使用互斥锁,则返回MY_ERROR_BUSY。如果确实成功获取了互斥锁,请在使用完该互斥锁后调用ReleaseMutex()。
预计到达时间:
如果WaitForSingleObject返回WAIT_OBJECT_0或WAIT_ABANDONED,则您拥有互斥锁(例如,它会发出信号),并且必须在调用CloseHandle之前调用ReleaseMutex放弃所有权(例如,取消对其发出信号)。
如果返回WAIT_TIMEOUT,则您不拥有该互斥锁,而只能调用CloseHandle。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| int FunctionExposedByDll()
{
HANDLE hMutexAPI = CreateMutex(0, 0, API_RUNNING_MUTEXT );
int rval = MY_ERROR_BUSY;
if ( hMutexAPI )
{
DWORD wait_success = WaitForSingleObject( hMutexAPI, 0 );
if ( wait_success == WAIT_OBJECT_0 || wait_success == WAIT_ABANDONED )
{
// actual function here
ReleaseMutex( hMutexAPI );
rval = SUCCESS_VALUE;
}
CloseHandle( hMutexAPI );
}
return rval;
} |
从此处开始,您应该花一些时间来熟悉基本的进程间通信原语:http://en.wikipedia.org/wiki/Mutex
再次预计
我认为C ++ 11可能已将IPC原语添加到标准库中,看来它们具有:
1 2
| http://en.cppreference.com/w/cpp/thread/mutex,
http://en.cppreference.com/w/cpp/thread/lock_guard |
如果您使用的是支持C ++ 11的编译器(例如Visual Studio 2013),请使用它们而不是系统原语。
还有一个编辑...
正如评论中指出的那样,C ++标准库原语不可用于进程间通信,而仅可用于同步同一进程中的线程。 (非常令人失望。)
相反,请尽可能使用Boost.Interprocess。
-
谢谢,我现在知道如何使用它,但我认为我不需要WaitForSingleObject且超时为0,部分原因是我将始终拥有有效的句柄。我应该简单地调用GetLastError(),这应该足够了,让我尝试一下。
-
不,仅仅因为它存在并不意味着它正在使用中。当进程A的时间段结束之前,进程A可能已经调用ReleaseMutex(),然后才可以调用CloseHandle()。然后,进程B调用CreateMutex()。然后您遇到了情况,进程A已完成,但进程B无法使用它。您不能将互斥锁的存在用作互斥锁。正确使用它。
-
我已经将WaitForSingleObject与0的事件超时一起使用,以查看是否已发出信号,但我不知道互斥量是什么意思,它没有SetEvent或ResetEvent来更改其状态!我很困惑!。我应该打电话给ReleaseMutex和CloseHandle吗?我以为以后就足够了。
-
只是检查ERROR_ALREADY_EXISTS对我有用,但是我有一个问题。似乎我必须在繁忙状态下第二次打开互斥锁时呼叫CloseHandle。否则,即使第一个调用完成并释放互斥锁,它也始终显示ERROR_ALREADY_EXISTS。但是当我第二次CloseHandle互斥锁并且第一个函数最终完成并想要关闭句柄时,我得到了第一次机会异常。如果可以删除它,我将有解决方案,但是我看不到如何删除它。
-
您应该做的是让每个应用程序调用一次具有相同互斥体名称的CreateMutex()(最好是在启动时),并且一次CloseHandle()具有一次(最好是在关机时)。然后,每个应用程序都想在进入DLL之前获取互斥锁时调用WaitForSingleObject()。如果WFSO成功,则根据需要调用DLL,然后调用ReleaseMutex()释放锁,然后将所需的结果返回给调用方。如果WFSO失败,如果由于WAIT_TIMEOUT而失败,则返回"忙",否则返回"错误"。
-
@RemyLebeau我希望互斥锁在dll内部,并且不希望第三方担心它。如果当前正在运行任何API,它将返回繁忙状态。我也更新了帖子。
-
我扩展了答案,以更全面地展示您需要做什么。
-
感谢@RobK我尝试了WaitForSingleObject,该方法非常有效!在我的代码中,仅调用ReleaseMutex和CloseHandle就不够了。请告诉我是否有必要致电ReleaseMutex谢谢!
-
您可以在加载DLL时创建互斥锁,然后在DLL卸载时将其关闭。请勿使用CloseHandle()释放通过WaitForSingleObject()获得的互斥锁,而必须使用ReleaseMutex()。请勿重复打开和关闭互斥锁,否则会破坏使用互斥锁进行同步的目的。
-
您应该在使用受保护资源的程序生命周期内保持互斥的句柄。如果您的程序将多次调用FunctionExposedByDll(),则在加载和卸载DLL时应在FunctionExposedByDll()外部创建并关闭句柄。而且您确实必须调用ReleaseMutex()。否则,将操作系统标记为未信号需要花费更长的时间。现在就正确地进行操作,以后由于不正确地执行操作而遇到问题时,您不会为自己生气。
-
您可能还考虑将互斥量包装在一个类中,该类在构造函数中调用CreateMutex(),在析构函数中调用CloseHandle(),并具有take()和release()成员函数,以及一个对互斥量进行引用的哨兵类。类作为其构造函数的参数,并在该互斥对象上调用take(),然后在其析构函数中调用release()。那就是处理互斥的线程安全RAII方法。它实际上写起来非常快。
-
感谢@RobK,我已经创建了AutoRelease类,该类在析构函数中释放互斥量,但是我发现了一个问题。此代码可在同一应用程序中运行,但是当我运行应用程序的两个实例时,它们不会在同一互斥锁上等待!有任何想法吗?谢谢
-
您还在使用命名的互斥锁吗?
-
@RobK抱歉,这是错误的警报,由于另一个问题,它看起来像这样。
-
我试图了解混乱的根源。 1)令人误解的想法是GetLastError()中的ERROR_ALREADY_EXISTS意味着已经有一个活动程序拥有该资源。 2)误导性的思想认为CloseHandle是产生或放弃资源的方式。 3)令人误解的想法是,成功完成WaitForSingleObject之后,必须随后调用CloseHandle以产生或放弃资源。一旦弄清了这些令人困惑的观点,就应该能够帮助人们更好地理解Windows API中的mutual exclusion原语。谢谢。
-
此答案的最新编辑不正确。 C ++ 11没有任何进程间原语。 std :: mutex用于线程同步,而不用于进程同步。
-
@PeterBloomfield,可悲的是,您似乎是正确的。我下结论了。似乎没有办法创建IPC所需的命名互斥体。这真令人失望。
无论如何,CreateMutex返回有效的句柄。也就是说,对!hMutexAPI的测试失败,并且您永远不会输入该子句。