Win32 - 多线程之线程同步

上一篇我们讲到:有一个全局变量`long long num = 0;`,然后创建了50个线程,其中一半执行`threadInc`函数,对num进行加1操作,另一半执行`threadDes`函数,进行减1操作。每个线程循环50万次,所以理论上如果没有任何竞争的话,最后的结果应该是0,因为加减次数相同。但实际运行的时候,结果可能不是0,而是其他数值,这说明存在数据竞争的问题。

为了解决多线程并发访问全局变量导致的数据竞争问题,可以使用 Windows API 的互斥对象(Mutex)来同步线程操作。

1、CreateMutex

CreateMutex 是 Windows API 中用于创建或打开一个互斥体(Mutex)对象的函数,其原型如下:

HANDLE CreateMutex(
  LPSECURITY_ATTRIBUTES lpMutexAttributes,  // 安全属性(通常为 NULL)
  BOOL                  bInitialOwner,      // 初始所有权(是否立即拥有互斥体)
  LPCSTR                lpName              // 互斥体名称(跨进程标识,可为 NULL)
);

参数说明

  1. lpMutexAttributes

    • 类型:LPSECURITY_ATTRIBUTES(指向 SECURITY_ATTRIBUTES 结构的指针)

    • 作用:定义互斥体的安全属性(如继承性),通常设为 NULL,表示使用默认安全设置。

  2. bInitialOwner

    • 类型:BOOL

    • 作用:指定调用线程是否立即拥有互斥体的所有权。

      • TRUE:调用线程直接获得互斥体,需手动调用 ReleaseMutex 释放。

      • FALSE(推荐):互斥体初始处于未锁定状态,线程需通过 WaitForSingleObject 请求所有权。

  3. lpName

    • 类型:LPCSTR(常量字符串指针)

    • 作用:为互斥体命名,用于跨进程共享。

      • 非空:创建命名互斥体(其他进程可通过相同名称访问)。

      • NULL:创建未命名的互斥体(仅限同一进程内使用)。

 

使用步骤:

(1)创建互斥体
在 main 函数中使用 CreateMutex 创建互斥体:

hMutex = CreateMutex(NULL, FALSE, NULL);
  • FALSE 表示主线程不立即拥有互斥体。

  • 如果创建失败,通过 GetLastError() 输出错误信息。

(2)线程函数中加锁/解锁

在 threadInc 和 threadDes 的循环中,每次操作 num 前加锁,操作后解锁:

WaitForSingleObject(hMutex, INFINITE);  // 请求锁
num += 1;  // 或 num -= 1
ReleaseMutex(hMutex);                   // 释放锁

(3)关闭互斥体句柄

在所有线程结束后,关闭互斥体句柄:

CloseHandle(hMutex);

 

以下是修改后的代码:

#include <stdio.h>
#include <Windows.h>
#include <process.h>

#define NUM_THREAD 50

unsigned WINAPI threadInc(void* arg);    // 加一操作
unsigned WINAPI threadDes(void* arg);    // 减一操作

// 全局变量
long long num = 0;
HANDLE hMutex;  // 互斥体句柄

int main(void) {
    HANDLE tHandles[NUM_THREAD];

    // 创建互斥体(未命名,初始状态为未锁定)
    hMutex = CreateMutex(NULL, FALSE, NULL);
    if (hMutex == NULL) {
        printf("CreateMutex error: %d\n", GetLastError());
        return 1;
    }

    // 创建50个线程(交替执行加/减操作)
    for (int i = 0; i < NUM_THREAD; i++) {
        if (i % 2) {
            tHandles[i] = (HANDLE)_beginthreadex(NULL, 0, threadInc, NULL, 0, NULL);
        } else {
            tHandles[i] = (HANDLE)_beginthreadex(NULL, 0, threadDes, NULL, 0, NULL);
        }
    }

    // 等待所有线程结束
    WaitForMultipleObjects(NUM_THREAD, tHandles, TRUE, INFINITE);

    // 关闭所有线程句柄和互斥体
    for (int i = 0; i < NUM_THREAD; i++) {
        CloseHandle(tHandles[i]);
    }
    CloseHandle(hMutex);

    printf("Final result: %lld\n", num);
    system("pause");
    return 0;
}

// 加一操作(通过互斥体同步)
unsigned WINAPI threadInc(void* arg) {
    for (int i = 0; i < 500000; i++) {
        WaitForSingleObject(hMutex, INFINITE);  // 请求锁
        num += 1;
        ReleaseMutex(hMutex);                   // 释放锁
    }
    return 0;
}

// 减一操作(通过互斥体同步)
unsigned WINAPI threadDes(void* arg) {
    for (int i = 0; i < 500000; i++) {
        WaitForSingleObject(hMutex, INFINITE);  // 请求锁
        num -= 1;
        ReleaseMutex(hMutex);                   // 释放锁
    }
    return 0;
}

运行结果:

 

posted @ 2024-12-30 09:54  [BORUTO]  阅读(32)  评论(0)    收藏  举报