CreateMutex 从不返回 ERROR_ALREADY_EXISTS

Posted

技术标签:

【中文标题】CreateMutex 从不返回 ERROR_ALREADY_EXISTS【英文标题】:CreateMutex never returns ERROR_ALREADY_EXISTS 【发布时间】:2014-05-08 16:52:01 【问题描述】:

我正在使用CreateMutex 来阻止多个应用程序同时运行某些功能。这些函数在 dll 中,因此可以由相同的应用程序或单独的应用程序调用。这个 dll 与硬件对话,所以如果另一个函数已经在运行,我想返回“忙碌”,而不是等待它。我认为最好的方法是使用 CreateMutex 而不是 OpenMutex 的组合。

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 的回答,我已将代码更改为:

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 稍后完成并想要关闭句柄时,我得到了第一次机会异常。我不明白我该如何避免这种情况!?

【问题讨论】:

【参考方案1】:

这不是使用CreateMutex() 的正确方法。 CreateMutex()(几乎)总是成功地将有效句柄返回给互斥体。从您链接的 MSDN 文档中:“如果互斥锁是命名互斥锁并且对象在此函数调用之前存在,返回值是现有对象的句柄,GetLastError 返回 ERROR_ALREADY_EXISTS...”

再次引用:“两个或多个进程可以调用 CreateMutex 来创建同名互斥锁。第一个进程实际创建互斥锁,后续具有足够访问权限的进程只需打开现有互斥锁的句柄。这使得多个进程能够获得同一个互斥锁的句柄,同时减轻了用户确保创建进程首先启动的责任。”

你要做的是使用CreateMutex()打开一个互斥锁的句柄,然后使用WaitForSingleObject()超时值为0来尝试取它。如果WaitForSingleObject() 无法获取互斥锁,则返回 MY_ERROR_BUSY。如果它确实成功获取了互斥锁,请在使用完成后调用ReleaseMutex() 解锁它。

预计到达时间:

如果 WaitForSingleObject 返回 WAIT_OBJECT_0 或 WAIT_ABANDONED,则您拥有互斥锁(例如,它变成有信号的),并且您必须在调用 CloseHandle 之前调用 ReleaseMutex 以放弃所有权(例如取消它的信号)。

如果它返回 WAIT_TIMEOUT,则您不拥有互斥锁,您可以调用 CloseHandle。

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

ETA 再次 我认为 C++ 11 可能已将 IPC 原语添加到标准库中,并且它们似乎具有:

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 在其时间片结束之前可能已经调用了ReleaseMutex(),然后才能调用CloseHandle()。然后进程 B 调用CreateMutex()。然后你会遇到进程 A 使用它,但进程 B 不能使用它的情况。您不能将互斥锁的存在用作互斥锁。正确使用。 您可以在加载DLL时创建互斥锁,然后在卸载DLL时关闭它。不要使用CloseHandle() 来释放您使用WaitForSingleObject() 获得的互斥锁,您必须使用ReleaseMutex() 代替。不要重复打开和关闭互斥锁,这违背了使用互斥锁进行同步的目的。 您应该在程序对使用受保护资源感兴趣的生命周期内保留一个互斥锁句柄。如果您的程序将多次调用 FunctionExposedByDll(),当您加载和卸载 DLL 时,您应该在 FunctionExposedByDll() 之外创建和关闭句柄。你真的必须调用 ReleaseMutex()。否则,操作系统将其标记为未发出信号需要更长的时间。现在就做对,以后当你因为做错而遇到问题时,你就不会生自己的气了。 @PeterBloomfield,可悲的是,您似乎是正确的。我匆忙下结论。似乎没有一种方法可以创建 IPC 所需的命名互斥锁。这令人失望。【参考方案2】:

CreateMutex 无论如何都会返回一个有效的句柄。也就是说,!hMutexAPI 的测试失败,您永远不会输入子句。

【讨论】:

以上是关于CreateMutex 从不返回 ERROR_ALREADY_EXISTS的主要内容,如果未能解决你的问题,请参考以下文章

CreateMutex函数 (转)

CreateMutex和WaitForSingleObject组合的有关问题

CreateMutex()参数问题

Delphi CreateMutex 防止程序多次运行

进程的互斥运行:CreateMutex函数实现只运行一个程序实例

如何从.Net调用win32 CreateMutex