使用互斥锁同步 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 个事件(将其命名为 Low 和 High)。
第一个线程:
-
写入数据
信号低事件
等待 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 个进程的主要内容,如果未能解决你的问题,请参考以下文章