使用互斥锁同步 2 个进程

Posted

技术标签:

【中文标题】使用互斥锁同步 2 个进程【英文标题】:Synchronize 2 processes using a mutex 【发布时间】:2017-11-15 12:17:27 【问题描述】:

我有 2 个进程:

    第一个创建一个memory mapped region,一个mutex 并生成 第二个过程。然后在memory mapped region中写入一些数字对。 第二个打开memory mapped region,opens the mutex然后读取process 1写的数字。

我打算第一个进程写入一对数字,第二个进程立即读取它。

process 2 似乎饿死了。

我做错了什么?

流程一:

#include "stdafx.h"
#include <windows.h>
#include <iostream>

using namespace std;

int main()

    DWORD memSize = 400 * sizeof(DWORD);
    HANDLE map_file = CreateFileMapping(NULL, NULL, PAGE_READWRITE, 0, memSize, TEXT("mem1"));

    if (map_file == NULL)
    
        _tprintf(_T("(Parent) File mapping is null\n"));
        return 1;
    

    char* map_ptr = (char *) MapViewOfFile(map_file, FILE_MAP_READ, 0, 0, 0);
    if (map_ptr == NULL)
    
        _tprintf(_T("(Parent) PTR is null \n"));
    

    HANDLE hMutex = CreateMutex(NULL, TRUE, _T("mt"));
    LPTSTR szCmdline = _tcsdup(TEXT("C:\\Users\\cristi\\source\\repos\\process_synchronization_reader\\Debug\\process_synchronization_reader.exe"));
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    ZeroMemory(&pi, sizeof(pi));

    if (!CreateProcess(NULL, szCmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
    
        _tprintf(_T("Process created\n"));
    

    _tprintf(_T("pare ca s-a creat"));
    for (int i = 1; i <= 200; ++i)
    
        WaitForSingleObject(hMutex, INFINITE);

        _tprintf(_T("(Parent %d) writing from the parent\n"), i);
        DWORD a, b;

        CopyMemory((LPVOID) &a, map_ptr, sizeof(DWORD));
        map_ptr += sizeof (DWORD);

        CopyMemory((LPVOID) &b, map_ptr, sizeof(DWORD));
        map_ptr += sizeof(DWORD);

        ReleaseMutex(hMutex);
    

    int n;
    cin >> n;
    CloseHandle(map_file);

    return 0;

流程2:

#include "stdafx.h"
#include <windows.h>

int main()

    HANDLE map_file = OpenFileMapping(FILE_MAP_READ, FALSE, TEXT("mem1"));

    if (map_file == NULL)
    
        _tprintf(_T("(Child) File mapping is null\n"));
        return 1;
    

    char* map_ptr = (char *) MapViewOfFile(map_file, FILE_MAP_READ, 0, 0, 0);

    if (map_ptr == NULL)
    
        _tprintf(_T("(Child) PTR is null \n"));
    

    _tprintf(_T("(CHILD) BEfore reading the first number\n"));
    HANDLE hMutex = OpenMutex(SYNCHRONIZE, TRUE, _T("mt"));
    for (int i = 1; i <= 200; i++)
           
        WaitForSingleObject(hMutex, INFINITE);
        DWORD a = i;
        DWORD b = 2 * i;

        CopyMemory((LPVOID) map_ptr, &a, sizeof(DWORD));
        map_ptr += sizeof(DWORD);

        CopyMemory((LPVOID) map_ptr, &b, sizeof(DWORD));
        map_ptr += sizeof(DWORD);

        _tprintf(_T("[================================================]\n"));
        _tprintf(_T("( %d %d )\n"), a, b);
        _tprintf(_T("[=================================================]\n"));

        ReleaseMutex(hMutex);
    

    return 0;

【问题讨论】:

我认为你需要 2 个互斥锁来完成这个:一个读互斥锁和一个写互斥锁。客户端必须等待写入互斥体才能读取,反之亦然。见here 这是有道理的。 您是否需要 1-2-1-2.. 访问共享数据的顺序? 【参考方案1】:

为了从共享内存中获得顺序写入/读取,我们需要 2 个事件(将其命名为 LowHigh)。

第一个线程:

    写入数据 信号事件 等待 High 事件或中断循环 转到 1

第二个线程:

    等待事件 读取数据 中断循环或发出信号事件 转到 1

与此解决方案不同,mutex 不能提供读/写序列。互斥锁保证在一个线程将访问共享数据(读取或写入)之前,另一个线程不会同时执行此操作。但这并不能防止连续多次写入或读取。真的 - 在进程 2 的开头插入消息框 - 在他第一次尝试获取互斥锁之前 - 第一个进程已经多次获取和释放互斥锁。或者如果一个线程将在释放和等待互斥锁之间暂停 - 同时另一个线程多次等待并释放它。所以代码看起来像:

struct SHARED_DATA

    ULONG id;
    ULONG nLoops;
    BOOL bTask;
;

DWORD proc2(SHARED_DATA* p)
   
    if (HANDLE hLowEvent = OpenEvent(SYNCHRONIZE, FALSE, L"LowEvent"))
    
        if (HANDLE hHighEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, L"hHighEvent"))
        
            ULONG id = GetCurrentThreadId();

            for(;;) 
            
                if (WaitForSingleObject(hLowEvent, INFINITE) != WAIT_OBJECT_0)
                
                    break;
                

                // ++ checking for sequence
                if (p->id == id)
                
                    __debugbreak();// must never be
                

                p->id = id;
                // -- checking for sequence

                if (!p->bTask)
                
                    // no more task
                    break;
                

                // task done
                p->bTask = FALSE;

                // signal to #1
                if (!SetEvent(hHighEvent))
                
                    break;
                
            

            CloseHandle(hHighEvent);
        
        CloseHandle(hLowEvent);
    

    return 0;


DWORD proc1(SHARED_DATA* p)

    if (HANDLE hLowEvent = CreateEvent(0, FALSE, FALSE, L"LowEvent"))
    
        if (HANDLE hHighEvent = CreateEvent(0, FALSE, FALSE, L"hHighEvent"))
        
            ULONG id = GetCurrentThreadId();

            p->nLoops = 0x1000;
            p->id = 0;
            p->bTask = FALSE;

            // exec proc2 here
            goto __1;

            do 
            
                if (WaitForSingleObject(hHighEvent, INFINITE) != WAIT_OBJECT_0)
                
                    break;
                

                if (p->bTask)
                
                    __debugbreak();
                

                // ++ checking for sequence
                if (p->id == id)
                
                    __debugbreak();// must never be
                
__1:
                p->id = id;
                // -- checking for sequence

                p->bTask = 0 < --p->nLoops;

                // signal to #2
                if (!SetEvent(hLowEvent))
                
                    break;
                

             while (p->nLoops);

            CloseHandle(hHighEvent);
        
        CloseHandle(hLowEvent);
    

    return 0;

【讨论】:

请务必在使用未记录的实施细节时指定。第二个代码 sn-p 中调用的函数没有官方文档或支持。 @IInspectable - 我明确指出,第二个代码 自 win8 以来不起作用。这个 api 坏了。第一个代码 sn-p(实际答案)有效且正确 说某些东西在 Windows 8 中不工作并不等于说它不支持在 Windows 8 之前的 Windows 版本中工作。这就是 "没有官方支持和记录” 的意思。仍然很难理解合同的概念,不是吗? @IInspectable - 但你怎么知道这是一个未记录的 api?可能其他人也以某种方式理解这一点?我不认为读者不理解这一点。无论如何,我不建议将此作为解决方案,也不建议使用它。从 win8(或 8.1)开始删除对 EventPair 的支持。我只是说这很有趣 - 就是这样的 api,在这里会很有用。但它还是坏掉了。 我知道 NtSetHighWaitLowEventPair 没有文档,因为 MSDN 上没有它的文档。此外,如果 undocumented.ntinternals.net 显示为搜索引擎中的第一个结果,这是一个非常强烈的指示。我不知道你为什么认为它不是无证的。无论如何,证明我错了,并发布一个链接到 official 文档。如果您不能,只需更新您的答案以明确说明使用的 API 调用未记录且不受支持。适用于任何版本的 Windows。【参考方案2】:

您创建一个最初拥有的互斥锁(CreateMutex 的第二个参数是TRUE),然后在其上调用一个等待函数。所以即使在调用ReleaseMutex之后,它仍然归第一个进程的主线程所有。

要么将参数更改为FALSE,要么在第一次循环迭代中跳过调用WaitForSingleObject

【讨论】:

将参数从TRUE更改为FALSE只会导致第二个进程崩溃。 @cristid9 那是因为您打开文件映射进行读取,然后将数据写入其中。实际上,您的第一个进程读取数据,第二个进程写入数据,您需要解决此问题。 然而这一切都无法解决进程 2 似乎正在挨饿。 @RbMm 实际上这确实解决了问题。只需要正确释放互斥锁即可。 @cristid9 - 不认为这可以解决问题。在进程#2 开始甚至执行之前,进程#1 已经可以多次获取和释放互斥锁。还有顺序,机器人进程如何访问共享内存是任意的。可以是连续的多次写入或读取。如果您需要准确订购 1-2-1-2,则需要 2 个事件

以上是关于使用互斥锁同步 2 个进程的主要内容,如果未能解决你的问题,请参考以下文章

使用互斥锁进行进程间同步 - 获取 AME

LinuxC线程pthread线程同步进程同步-互斥量信号量条件变量读写锁文件锁

读写锁 与 互斥锁

Python多进程,同步互斥,信号量,锁补充上一篇文章

Python下进程同步之互斥锁信号量事件机制 𪕽

互斥锁和信号量