如何在两个线程中使用 Windows.h 互斥体

问题描述 投票:0回答:1

我在两个不同的线程中使用互斥体时遇到问题。

我有两个线程函数:一个生产者,它从用户接收数据并将其写入全局变量;以及一个消费者,它从全局变量接收数据,处理它并将其返回给用户。

因此,我创建了两个互斥体:“生成的信息”和“处理的信息”。第一个状态是当从用户接收到信息并且已经记录在全局变量中时处于“打开”状态,而当信息尚未发生时处于“关闭”状态。第二个状态是当信息被处理并向用户显示时处于“打开”状态,而当信息尚未发生时处于“关闭”状态。

因此,生产者线程等待“已处理信息”互斥体打开以开始从用户接收新信息。当接收到信息时,生产者线程打开“信息生成”互斥锁。

反过来,消费者线程等待“信息生成”互斥体打开才能开始处理它。当信息被处理时,消费者线程打开“信息处理”互斥锁。

但是!当我运行这段代码时,消费者线程处于等待状态,生产者线程不停地运行,消费者线程没有开始工作。

#include <windows.h>
#include <stdio.h>
#include <iostream>

HANDLE hMutexReady,   // Mutex "information generated"
       hMutexHandled, // Mutex "information processed"

// Producer thread function
DWORD WINAPI producer(LPVOID param) {
    while (true) {
        // Capture the "information processed" mutex    
        WaitForSingleObject(hMutexHandled, INFINITE);

        // Get some data from user and store it in global variable

        // Release the "information generated" mutex
        ReleaseMutex(hMutexReady);
    }
    return 0;
}

// Consumer thread function
DWORD WINAPI consumer(LPVOID param) {
    while (true) {
        // Caprute the "information generated" mutex
        WaitForSingleObject(hMutexReady, INFINITE);

        // Get data from global variable, process it and return to user

        // Release the "information processed" mutex
        ReleaseMutex(hMutexHandled);
    }
    return 0;
}


int _tmain(int argc, _TCHAR* argv[]) {
    // Creating mutexes with default valuesю
    // all mutexes are created in the "open" state
    hMutexReady = CreateMutex(NULL, false, NULL);
    hMutexHandled = CreateMutex(NULL, false, NULL);

    // We close the "information generated" mutex, because it hasn't been generated yet
    WaitForSingleObject(hMutexReady, INFINITE);

    // Creating producer and consumer threads
    HANDLE hProducer = CreateThread(NULL, 0, producer, NULL, 0, NULL);
    HANDLE hConsumer = CreateThread(NULL, 0, consumer, NULL, 0, NULL);

    HANDLE hThread[2];
    hThread[0] = hProducer;
    hThread[1] = hConsumer;

    // Waiting for both threads to complete
    WaitForMultipleObjects(2, hThread, true, INFINITE);

    CloseHandle(hProducer);
    CloseHandle(hConsumer);

    return 0;
}

请帮助我!

重要!该代码适用于我的大学,我需要使用 Windows 库中的互斥体来准确实现此功能。

c++ winapi mutex
1个回答
0
投票

对于初学者来说,您有三个线程。

  • main
  • producer
  • consumer
    .

要记住的最重要的规则是,任何通过

WaitForSingleObject
获取互斥体的线程都可以是调用
ReleaseMutex
的唯一线程。任何尝试在不属于该线程或根本不属于该线程的互斥体上调用
ReleaseMutex
的线程基本上都是一个错误,并且
ReleaseMutex
的返回值几乎肯定是
FALSE

正如您所见,您对

ReleaseMutex(hMutexHandled)
producer
调用失败了,因为该线程不是实际调用
WaitForSingleObject
的线程。

其次,

consumer
WaitForSingleObject(hMutexReady, INFINITE)
上永远被阻塞,因为
main
consumer
启动之前获取了该互斥体。

如果在生产者和消费者之间只有 1 个变量来保护,并且目的是生产者需要等待消费者完成才能生成新值,那么这很容易。你只需要一个互斥体。但为了在线程之间发出就绪信号,您不需要额外的互斥锁,您需要一个事件对象,它是经典条件变量的变体。

#include <windows.h>
#include <stdio.h>
#include <iostream>

BOOL dataPending; // is set to true when new data is produced

HANDLE hMutex;
HANDLE hEventDataProduced;
HANDLE hEventDataConsumed;


DWORD WINAPI producer(LPVOID param) {

    WaitForSingleObject(hMutex, INFINITE);

    while (true) {

        // wait for consumer thread to accept any pending data
        while (dataPending == TRUE) {
            ReleaseMutex(hMutex);
            WaitForSingleObject(hEventDataConsumed, INFINITE); // wait for consumer to indicate its done
            WaitForSingleObject(hMutex, INFINITE);
        }

        // Not shown: Generate the "the data" however that's meant to happen
        // and assign to whatever global or shared variable

        // not shown breaking out of the loop when a certain condition is met

        dataPending = TRUE;

        SetEvent(hEventDataProduced);  // wake up consumer thread

        std::cout << "Produced\n";
    }

    ReleaseMutex(hMutex);
}

DWORD WINAPI consumer(LPVOID param) {

    WaitForSingleObject(hMutex, INFINITE);

    while (true) {

        // not shown breaking out of the loop when a certain condition is met

        // wait for producer thread to give us something
        while (dataPending == FALSE) {
            ReleaseMutex(hMutex);
            WaitForSingleObject(hEventDataProduced, INFINITE);
        }

        // consume the data from the global/shared memory however that's supposed to be done

        dataPending = FALSE;

        std::cout << "Consumed\n";
        SetEvent(hEventDataConsumed); // notify producer thread it can continue
    }

    ReleaseMutex(hMutex);
}


int main(int argc, char* argv[]) {

    // 1 mutex for guarding the data and the "dataPending" variable
    hMutex = CreateMutex(NULL, false, NULL);

    // two event objects to signal between threads about state
    hEventDataProduced = CreateEvent(NULL, FALSE, FALSE, NULL);
    hEventDataConsumed = CreateEvent(NULL, FALSE, FALSE, NULL);

    dataPending = FALSE;

    // Creating producer and consumer threads
    HANDLE hProducer = CreateThread(NULL, 0, producer, NULL, 0, NULL);
    HANDLE hConsumer = CreateThread(NULL, 0, consumer, NULL, 0, NULL);

    HANDLE hThread[2] = { hProducer, hConsumer };

    // Waiting for both threads to complete
    WaitForMultipleObjects(2, hThread, true, INFINITE);

    CloseHandle(hProducer);
    CloseHandle(hConsumer);

    return 0;
}

从技术上讲,对于 Win32 事件,您不需要

dataPending
变量,因为虚假唤醒实际上并不是事件的问题。但我认为它使代码更具可读性和更容易理解。对于根除错误也很有用。

© www.soinside.com 2019 - 2024. All rights reserved.