【问题标题】:Problem with multi-threading and waiting on events多线程和等待事件的问题
【发布时间】:2021-11-11 14:24:06
【问题描述】:

我的代码有问题:

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <windows.h>
#include <string.h>
#include <math.h>

HANDLE event;
HANDLE mutex;

int runner = 0;

DWORD WINAPI thread_fun(LPVOID lpParam) {
    int* data = (int*)lpParam;
    for (int j = 0; j < 4; j++) { //this loop necessary in order to reproduce the issue
        if ((data[2] + 1) == data[0]) { // if it is last thread 
            while (1) {
                WaitForSingleObject(mutex, INFINITE);
                if (runner == data[0] - 1) { // if all other thread reach event break
                    ReleaseMutex(mutex);
                    break;
                }
                printf("Run:%d\n", runner);
                ReleaseMutex(mutex);
                Sleep(10);
            }
            printf("Check Done:<<%d>>\n", data[2]);
            runner = 0;
            PulseEvent(event); // let all other threads continue
        }
        else { // if it is not last thread
            WaitForSingleObject(mutex, INFINITE);
            runner++;
            ReleaseMutex(mutex);
            printf("Wait:<<%d>>\n", data[2]);
            WaitForSingleObject(event, INFINITE); // wait till all other threads reach this stage
            printf("Exit:<<%d>>\n", data[2]);
        }
    }
    return 0;
}


int main()
{
    event = CreateEvent(NULL, TRUE, FALSE, NULL);
    mutex = CreateMutex(NULL, FALSE, NULL);
    SetEvent(event);
    int data[3] = {2,8}; //0 amount of threads //1 amount of numbers
    HANDLE t[10000];
    int ThreadData[1000][3];
    for (int i = 0; i < data[0]; i++) {
        memcpy(ThreadData[i], data, sizeof(int) * 2); // copy amount of threads and amount of numbers to the threads data
        ThreadData[i][2] = i; // creat threads id
        LPVOID ThreadsData = (LPVOID)(&ThreadData[i]);
        t[i] = CreateThread(0, 0, thread_fun, ThreadsData, 0, NULL);
        if (t[i] == NULL)return 0;
    }
    while (1) {
        DWORD res = WaitForMultipleObjects(data[0], t, true, 1000);
        if (res != WAIT_TIMEOUT) break;
    }
    for (int i = 0; i < data[0]; i++)CloseHandle(t[i]); // close all threads
    CloseHandle(event); // close event
    CloseHandle(mutex); //close mutex
    printf("Done");
}

主要思想是等到除一个之外的所有线程都到达事件并在那里等待,同时最后一个线程必须释放它们等待。

但是代码不能可靠地工作。 10 次中有 1 次正确结束,9 次卡在 while(1) 中。在不同的尝试中,while (printf("Run:%d\n", runner);) 中的printf 会打印不同数量的跑步者(0 和 3)。

可能是什么问题?

【问题讨论】:

  • 在您的问题中,您声明 data[2] + 1 等于当前线程的数量。这是否意味着data 为所有线程引用不同的内存位置?
  • @AndreasWenzel 不,每个线程都有自己的数据:int ThreadData[1000][3]; ... memcpy(ThreadData[i], data, sizeof(int) * 2); ThreadData[i][2] = i; LPVOID ThreadsData = (LPVOID)(&amp;ThreadData[i]); t[i] = CreateThread(0, 0, thread_fun, ThreadsData, 0, NULL);
  • 我在发布的代码中没有发现任何问题。因此,我怀疑问题出在您没有向我们展示的代码中。如果可能,请提供问题的minimal reproducible example
  • @AndreasWenzel 我添加了我的代码的最小示例
  • 您是否验证了新的最小代码确实重现了问题?它是否仍然为runner 打印5

标签: c++ multithreading winapi events mutex


【解决方案1】:

正如我们在 cmets 部分中发现的那样,问题在于尽管事件是在初始状态为未发出信号的情况下创建的

event = CreateEvent(NULL, TRUE, FALSE, NULL);

它被立即设置为信号状态:

SetEvent(event);

因此,至少在循环的第一次迭代中,当j == 0 时,第一个工作线程不会等待第二个工作线程,从而导致race condition

此外,您的代码存在以下问题值得一提(尽管这些问题不是您的问题的原因):

  1. 根据Microsoft documentation on PulseEvent,不应使用该函数,因为它可能不可靠并且主要用于向后兼容。根据文档,您应该改用condition variables
  2. 在您的函数thread_fun 中,最后一个线程在循环中锁定和释放互斥锁。这可能很糟糕,因为互斥锁不能保证是公平的,这可能会导致其他线程永远无法获取互斥锁。尽管在每次循环迭代中调用一次Sleep(10); 可以减轻这种可能性,但它仍然不是理想的解决方案。更好的解决方案是使用条件变量,以便线程仅在另一个线程实际发出可能更改的信号时检查变量runner 的更改。出于性能原因,这样的解决方案也会更好。

【讨论】:

    猜你喜欢
    • 2020-10-04
    • 2017-04-01
    • 2011-07-21
    • 1970-01-01
    • 1970-01-01
    • 2013-02-03
    • 1970-01-01
    • 2023-03-11
    • 2014-11-03
    相关资源
    最近更新 更多