WINAPI 事件对象意外行为

WINAPI Event Object unexpected behaviour

我有 2 个程序。 第一个程序将打开第二个程序,还将创建 2 个用于同步的事件对象。 第二个(要打开的那个)将打开这 2 个事件对象。 这两个程序都会 运行 一个 for 循环来模拟从内存中写入和读取操作。

方案一:

    read = CreateEvent(NULL, false, false, "READ");
    write = CreateEvent(NULL, false, false, "WRITE");

    CreateProcess("PATH_TO_EXE", NULL, NULL, NULL, FALSE, NULL, 0, NULL, &startupInfo, &processInformation);

    for (int i = 1; i <= 100; i++)
    {
        printf("Wrote data to memory\n");
        SetEvent(write);
        WaitForSingleObject(read, INFINITE);
    }

节目二:

    HANDLE read, write;

    read = OpenEvent(EVENT_MODIFY_STATE, false, "READ");
    write = OpenEvent(EVENT_MODIFY_STATE, false, "WRITE");

    for (int i = 1; i <= 100; i++)
    {
        WaitForSingleObject(write, INFINITE);
        printf("Read data from memory.\n");
        SetEvent(read);
    }

我希望输出为:

Wrote data to memory.
Read data from memory.
Wrote data to memory.
Read data from memory.
       ....

但实际输出是这样的:

Wrote data to memory.
Read data from memory.
Read data from memory.
Read data from memory.
Read data from memory.
Read data from memory.
Read data from memory.
Read data from memory.
Wrote data to memory.
Read data from memory.
Wrote data to memory.
Wrote data to memory.
Read data from memory.
      ...

有时它会挂起,这意味着死锁。但我不确定这怎么可能。有帮助吗?

for WaitForSingleObject - 对象句柄(第一个参数)必须具有 SYNCHRONIZE 访问权限。否则 api 失败 ERROR_ACCESS_DENIED。但是你打电话给

write = OpenEvent(EVENT_MODIFY_STATE, false, "WRITE");

请求的访问权限不包括您需要的 SYNCHRONIZE,并包括在此代码中您确实不需要的 EVENT_MODIFY_STATE。所以你需要将代码更改为

write = OpenEvent(SYNCHRONIZE, false, "WRITE");

您也不检查任何 api 调用的结果。如果你这样做 - 你只看到 WaitForSingleObject(write, INFINITE); return WAIT_FAILEDGetLastError() == ERROR_ACCESS_DENIED.

另外,如果您需要通过这 2 个带有子进程的事件进行 ipc - 最好创建未命名和继承的它,并通过命令行将其值传递给子进程。如果您只想测试事件工作逻辑 - 更容易使用单独的线程而不是新进程。此测试代码可能如下所示:

ULONG WINAPI child(void* p)
{
    if (HANDLE read = OpenEvent(EVENT_MODIFY_STATE, false, L"READ"))
    {
        if (HANDLE write = OpenEvent(SYNCHRONIZE, false, L"WRITE"))
        {
            ULONG i = (ULONG)(ULONG_PTR)p;
            do 
            {
                if (WaitForSingleObject(write, INFINITE) == WAIT_FAILED){
                    DbgPrint("2:%u\n", GetLastError());
                    break;
                }
                DbgPrint("Read data from memory.\n");
                if (!SetEvent(read)){
                    DbgPrint("3:%u\n", GetLastError());
                    break;
                }
            } while (--i);

            CloseHandle(write);
        }
        CloseHandle(read);
    }
    return 0;
}

void bfg()
{
    if (HANDLE read = CreateEvent(NULL, false, false, L"READ"))
    {
        if (HANDLE write = CreateEvent(NULL, false, false, L"WRITE"))
        {
            ULONG i = 16;

            if (HANDLE hThread = CreateThread(0, 0, child, (PVOID)(ULONG_PTR)i, 0, 0))
            {
                do 
                {
                    DbgPrint("Wrote data to memory\n");
                    if (!SetEvent(write) || WaitForSingleObject(read, INFINITE) == WAIT_FAILED){
                        DbgPrint("1:%u\n", GetLastError());
                        break;
                    }
                } while (--i);

                WaitForSingleObject(hThread, INFINITE);

                CloseHandle(hThread);
            }

            CloseHandle(write);
        }
        CloseHandle(read);
    }
}

有趣的是,早期(在 win 8.1 之前)是 windows 中的 EventPair 对象,它是为此类任务设计的。但未知原因已将其删除