等到另一个进程锁定然后解锁 Win32 互斥锁

Posted

技术标签:

【中文标题】等到另一个进程锁定然后解锁 Win32 互斥锁【英文标题】:Waiting until another process locks and then unlocks a Win32 mutex 【发布时间】:2014-11-26 19:21:18 【问题描述】:

我试图判断生产者进程何时访问共享的 Windows 互斥锁。发生这种情况后,我需要锁定同一个互斥体并处理相关数据。 Windows 中是否有内置方法可以做到这一点,没有一个荒谬的循环?

我知道这样做的结果可以通过在生产者进程中创建自定义 Windows 事件来实现,但我想尽可能避免更改此程序代码。

我相信会起作用(以一种非常低效的方式)是这样的(注意:这不是我的真实代码,我知道这有 10 种不同的事情非常错误;我想避免做这样的事情) :

#include <Windows.h>

int main() 
    HANDLE h = CreateMutex(NULL, 0, "name");
    if(!h) return -1;

    int locked = 0;
    while(true) 
        if(locked) 
            //can assume it wont be locked longer than a second, but even if it does should work fine
            if(WaitForSingleObject(h, 1000) == WAIT_OBJECT_0) 
                // do processing...
                locked = 0;
                ReleaseMutex(h);
            
        // oh god this is ugly, and wastes so much CPU...
         else if(!(locked = WaitForSingleObject(h, 0) == WAIT_TIMEOUT)) 
            ReleaseMutex(h);
        
    
    return 0;

如果出于某种原因使用 C++ 有更简单的方法,我的代码实际上就是这样。这个例子在 C 中更容易构建。

【问题讨论】:

也使用事件 (CreateEvent),生产者可以设置它,让您的其他进程知道是时候处理数据了。 其实根据生产者的写法,你的代码可能会死锁。 这是一种根本性的破坏方法。您不仅会烧掉 100% 的内核,还可能完全错过看到另一个线程实际获取和释放互斥锁的情况。没有办法,你需要重新设计你的方法。 我知道这种方法很糟糕。事件会更好100%。但我想完全避免更改生产者代码。我的问题可能措辞不好。我的意思是,是否有一种方法可以判断互斥锁何时被锁定/解锁,而无需像我给出的示例那样做一些愚蠢的事情。 您可以检查“关联数据”以查看是否有任何变化,而不是检查互斥锁?您已经有了“处理”,所以我假设您可以访问生成的数据。问题是,数据是什么形式的,你能检测出是否产生了新数据吗?如果您认为产生了新数据,您可以获取互斥体并处理数据。 【参考方案1】:

如果需要高效共享,您将无法避免更换生产者。你的设计存在根本缺陷。

生产者需要能够在数据准备好被消费时向消费者发出信号,并确保它在忙于消费时不会改变数据。你不能单独使用一个互斥锁来做到这一点。

最好的方法是让生产者在数据准备好时设置一个事件,并让消费者在数据被消费后重置事件。 使用互斥锁来同步对数据的访问,而不是发出数据准备就绪的信号。

#include <Windows.h>

int main()

    HANDLE readyEvent = CreateEvent(NULL, TRUE, FALSE, "ready");
    if (!readyEvent) return -1;

    HANDLE mutex = CreateMutex(NULL, FALSE, "name");
    if (!mutex) return -1;

    while(true)
    
        if (WaitForSingleObject(readyEvent, 1000) == WAIT_OBJECT_0)
        
            if (WaitForSingleObject(mutex, 1000) == WAIT_OBJECT_0)
            
                // process as needed...
                ResetEvent(readyEvent);
                ReleaseMutex(mutex);
            
        
    

    return 0;

如果您无法更改生产者以使用事件,那么至少为数据本身添加一个标志。生产者可以锁定互斥体,更新数据和标志,以及解锁互斥体。然后,消费者必须定期锁定互斥锁,检查标志并在标志设置时读取新数据,重置标志并解锁互斥锁。

#include <Windows.h>

int main()

    HANDLE mutex = CreateMutex(NULL, FALSE, "name");
    if (!mutex) return -1;

    while(true)
    
        if (WaitForSingleObject(mutex, 1000) == WAIT_OBJECT_0)
        
            if (ready)
            
                // process as needed...
                ready = false;
            
            ReleaseMutex(mutex);
        
    

    return 0;

因此,无论哪种方式,您的逻辑都必须在生产者和消费者中进行调整。

否则,如果你根本改变不了生产者,那你只能单独改变消费者,简单地周期性地检查数据是否有变化:

#include <Windows.h>

int main()

    HANDLE mutex = CreateMutex(NULL, 0, "name");
    if (!mutex) return -1;

    while(true)
    
        if (WaitForSingleObject(mutex, 1000) == WAIT_OBJECT_0)
        
            // check data for changes
            // process new data as needed
            // cache results for next time...
            ReleaseMutex(mutex);
        
    

    return 0;

【讨论】:

所以我听到的是,如果我不想做傻事,我就无法避免更换制作人。知道了。你是什​​么意思使用信号量来支持多个客户端的事件(如果我要更改生产者,我不妨做对)?【参考方案2】:

棘手。我要回答底层的问题:内存是什么时候写的?

这可以通过四步解决方案来观察:

    在被监视的进程中注入一个 DLL 为STATUS_GUARD_PAGE_VIOLATION 添加向量异常处理程序 在 2 MB 内存范围内设置保护页位(发现这可能是一个挑战) 从向量异常处理程序,通知您的进程并重新建立保护位(一次性)

如果图像总是完全重写,您可能只需要一个保护页。

【讨论】:

以上是关于等到另一个进程锁定然后解锁 Win32 互斥锁的主要内容,如果未能解决你的问题,请参考以下文章

PThread 强大的互斥锁不起作用

未解锁锁定的互斥锁

12.swoole学习笔记--锁机制

Pthread互斥锁由不同的线程解锁

进程崩溃时未释放 Win32 命名互斥锁

线程因互斥锁而崩溃