跳转至

同步对象API

概述

在多线程编程中,同步对象用于协调多个线程对共享资源的访问,防止竞争条件和数据不一致。Windows提供了多种同步机制。

同步对象类型

  • 临界区(Critical Section):最快,只能用于同一进程内的线程同步
  • 互斥体(Mutex):可用于不同进程间的线程同步
  • 信号量(Semaphore):控制对有限资源的并发访问数量
  • 事件(Event):用于线程间的通知和等待
  • 可等待定时器(Waitable Timer):定时通知

同步对象比较

对象 跨进程 开销 适用场景
临界区 最低 同进程内的高速同步
互斥体 中等 跨进程互斥访问
信号量 中等 资源计数控制
事件 中等 条件等待和通知

临界区对象

InitializeCriticalSection

C++
1
2
3
void InitializeCriticalSection(
    LPCRITICAL_SECTION lpCriticalSection
);

示例

C++
CRITICAL_SECTION cs;
InitializeCriticalSection(&cs);

InitializeCriticalSectionAndSpinCount

C++
1
2
3
4
BOOL InitializeCriticalSectionAndSpinCount(
    LPCRITICAL_SECTION lpCriticalSection,
    DWORD              dwSpinCount
);

参数: - dwSpinCount:旋转计数,在阻塞前尝试获取锁的次数

SetCriticalSectionSpinCount

C++
1
2
3
4
DWORD SetCriticalSectionSpinCount(
    LPCRITICAL_SECTION lpCriticalSection,
    DWORD              dwSpinCount
);

EnterCriticalSection

C++
1
2
3
void EnterCriticalSection(
    LPCRITICAL_SECTION lpCriticalSection
);

EnterCriticalSection

进入临界区。如果临界区已被其他线程占用,调用线程将阻塞等待。

示例

C++
1
2
3
EnterCriticalSection(&cs);
// 临界区代码
LeaveCriticalSection(&cs);

TryEnterCriticalSection

C++
1
2
3
BOOL TryEnterCriticalSection(
    LPCRITICAL_SECTION lpCriticalSection
);

返回值: - 非零:成功进入 - 零:临界区已被占用

示例

C++
1
2
3
4
5
6
7
if (TryEnterCriticalSection(&cs)) {
    // 成功进入临界区
    // ...
    LeaveCriticalSection(&cs);
} else {
    // 临界区被占用,执行其他操作
}

LeaveCriticalSection

C++
1
2
3
void LeaveCriticalSection(
    LPCRITICAL_SECTION lpCriticalSection
);

DeleteCriticalSection

C++
1
2
3
void DeleteCriticalSection(
    LPCRITICAL_SECTION lpCriticalSection
);

完整示例

C++
#include <windows.h>
#include <stdio.h>

CRITICAL_SECTION g_cs;
int g_counter = 0;

DWORD WINAPI ThreadFunc(LPVOID param) {
    for (int i = 0; i < 10000; i++) {
        EnterCriticalSection(&g_cs);
        g_counter++;
        LeaveCriticalSection(&g_cs);
    }
    return 0;
}

int main() {
    InitializeCriticalSection(&g_cs);
    
    HANDLE threads[10];
    for (int i = 0; i < 10; i++) {
        threads[i] = CreateThread(NULL, 0, ThreadFunc, NULL, 0, NULL);
    }
    
    WaitForMultipleObjects(10, threads, TRUE, INFINITE);
    
    printf("Counter: %d\n", g_counter);
    
    DeleteCriticalSection(&g_cs);
    for (int i = 0; i < 10; i++) {
        CloseHandle(threads[i]);
    }
    
    return 0;
}

互斥体对象

CreateMutex

C++
1
2
3
4
5
HANDLE CreateMutex(
    LPSECURITY_ATTRIBUTES lpMutexAttributes,
    BOOL                  bInitialOwner,
    LPCTSTR               lpName
);

参数: - lpMutexAttributes:安全属性 - bInitialOwner:调用线程是否初始拥有互斥体 - lpName:互斥体名称(用于跨进程)

示例

C++
1
2
3
4
HANDLE hMutex = CreateMutex(NULL, FALSE, L"MyMutex");
if (hMutex == NULL) {
    printf("CreateMutex failed: %d\n", GetLastError());
}

OpenMutex

C++
1
2
3
4
5
HANDLE OpenMutex(
    DWORD   dwDesiredAccess,
    BOOL    bInheritHandle,
    LPCTSTR lpName
);

ReleaseMutex

C++
1
2
3
BOOL ReleaseMutex(
    HANDLE hMutex
);

跨进程互斥示例

C++
// 进程1:创建互斥体
#include <windows.h>
#include <stdio.h>

int main() {
    HANDLE hMutex = CreateMutex(NULL, FALSE, L"Global\\MyMutex");
    
    printf("Waiting for mutex...\n");
    WaitForSingleObject(hMutex, INFINITE);
    printf("Got mutex, working...\n");
    
    Sleep(5000);  // 模拟工作
    
    ReleaseMutex(hMutex);
    CloseHandle(hMutex);
    
    return 0;
}
C++
// 进程2:打开互斥体
#include <windows.h>
#include <stdio.h>

int main() {
    HANDLE hMutex = OpenMutex(SYNCHRONIZE, FALSE, L"Global\\MyMutex");
    
    if (hMutex == NULL) {
        printf("OpenMutex failed: %d\n", GetLastError());
        return -1;
    }
    
    printf("Waiting for mutex...\n");
    WaitForSingleObject(hMutex, INFINITE);
    printf("Got mutex, working...\n");
    
    ReleaseMutex(hMutex);
    CloseHandle(hMutex);
    
    return 0;
}

信号量对象

CreateSemaphore

C++
1
2
3
4
5
6
HANDLE CreateSemaphore(
    LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
    LONG                  lInitialCount,
    LONG                  lMaximumCount,
    LPCTSTR               lpName
);

参数: - lInitialCount:初始计数 - lMaximumCount:最大计数

示例

C++
// 创建信号量:最多3个线程同时访问
HANDLE hSem = CreateSemaphore(NULL, 3, 3, NULL);

OpenSemaphore

C++
1
2
3
4
5
HANDLE OpenSemaphore(
    DWORD   dwDesiredAccess,
    BOOL    bInheritHandle,
    LPCTSTR lpName
);

ReleaseSemaphore

C++
1
2
3
4
5
BOOL ReleaseSemaphore(
    HANDLE hSemaphore,
    LONG   lReleaseCount,
    LPLONG lpPreviousCount
);

示例

C++
LONG prevCount;
ReleaseSemaphore(hSem, 1, &prevCount);  // 释放1个资源

连接池示例

C++
#include <windows.h>
#include <stdio.h>

HANDLE hSemaphore;
CRITICAL_SECTION cs;

DWORD WINAPI WorkerThread(LPVOID param) {
    int id = (int)(LONG_PTR)param;
    
    printf("Thread %d: Waiting for connection...\n", id);
    WaitForSingleObject(hSemaphore, INFINITE);
    
    printf("Thread %d: Got connection, working...\n", id);
    Sleep(2000);  // 模拟工作
    
    printf("Thread %d: Releasing connection...\n", id);
    ReleaseSemaphore(hSemaphore, 1, NULL);
    
    return 0;
}

int main() {
    // 创建信号量:最多3个连接
    hSemaphore = CreateSemaphore(NULL, 3, 3, NULL);
    InitializeCriticalSection(&cs);
    
    // 创建10个工作线程
    HANDLE threads[10];
    for (int i = 0; i < 10; i++) {
        threads[i] = CreateThread(NULL, 0, WorkerThread, (LPVOID)(LONG_PTR)i, 0, NULL);
    }
    
    WaitForMultipleObjects(10, threads, TRUE, INFINITE);
    
    DeleteCriticalSection(&cs);
    CloseHandle(hSemaphore);
    for (int i = 0; i < 10; i++) {
        CloseHandle(threads[i]);
    }
    
    return 0;
}

事件对象

CreateEvent

C++
1
2
3
4
5
6
HANDLE CreateEvent(
    LPSECURITY_ATTRIBUTES lpEventAttributes,
    BOOL                  bManualReset,
    BOOL                  bInitialState,
    LPCTSTR               lpName
);

参数: - bManualReset: - TRUE:手动重置事件 - FALSE:自动重置事件 - bInitialState:初始状态

手动重置 vs 自动重置

类型 SetEvent后 适用场景
手动重置 一直保持触发,需手动ResetEvent 唤醒多个等待线程
自动重置 唤醒一个线程后自动重置 唤醒单个等待线程

OpenEvent

C++
1
2
3
4
5
HANDLE OpenEvent(
    DWORD   dwDesiredAccess,
    BOOL    bInheritHandle,
    LPCTSTR lpName
);

SetEvent

C++
1
2
3
BOOL SetEvent(
    HANDLE hEvent
);

ResetEvent

C++
1
2
3
BOOL ResetEvent(
    HANDLE hEvent
);

PulseEvent

C++
1
2
3
BOOL PulseEvent(
    HANDLE hEvent
);

PulseEvent

设置事件后立即重置。不推荐使用,可能丢失信号。

生产者-消费者示例

C++
#include <windows.h>
#include <stdio.h>
#include <queue>

const int BUFFER_SIZE = 5;
std::queue<int> buffer;
CRITICAL_SECTION cs;
HANDLE hProducerEvent;  // 生产者事件
HANDLE hConsumerEvent;  // 消费者事件

DWORD WINAPI Producer(LPVOID param) {
    for (int i = 1; i <= 20; i++) {
        EnterCriticalSection(&cs);
        
        while (buffer.size() >= BUFFER_SIZE) {
            LeaveCriticalSection(&cs);
            WaitForSingleObject(hProducerEvent, INFINITE);
            EnterCriticalSection(&cs);
        }
        
        buffer.push(i);
        printf("Produced: %d (size: %zu)\n", i, buffer.size());
        
        SetEvent(hConsumerEvent);  // 通知消费者
        
        LeaveCriticalSection(&cs);
        Sleep(100);
    }
    
    // 结束信号
    EnterCriticalSection(&cs);
    buffer.push(-1);  // 特殊标记
    SetEvent(hConsumerEvent);
    LeaveCriticalSection(&cs);
    
    return 0;
}

DWORD WINAPI Consumer(LPVOID param) {
    while (true) {
        EnterCriticalSection(&cs);
        
        while (buffer.empty()) {
            LeaveCriticalSection(&cs);
            WaitForSingleObject(hConsumerEvent, INFINITE);
            EnterCriticalSection(&cs);
        }
        
        int item = buffer.front();
        buffer.pop();
        
        if (item == -1) {
            LeaveCriticalSection(&cs);
            break;
        }
        
        printf("Consumed: %d (size: %zu)\n", item, buffer.size());
        SetEvent(hProducerEvent);  // 通知生产者
        
        LeaveCriticalSection(&cs);
        Sleep(150);
    }
    
    return 0;
}

int main() {
    InitializeCriticalSection(&cs);
    hProducerEvent = CreateEvent(NULL, FALSE, TRUE, NULL);   // 自动重置,初始有信号
    hConsumerEvent = CreateEvent(NULL, FALSE, FALSE, NULL);  // 自动重置,初始无信号
    
    HANDLE hProducer = CreateThread(NULL, 0, Producer, NULL, 0, NULL);
    HANDLE hConsumer = CreateThread(NULL, 0, Consumer, NULL, 0, NULL);
    
    WaitForSingleObject(hProducer, INFINITE);
    WaitForSingleObject(hConsumer, INFINITE);
    
    CloseHandle(hProducer);
    CloseHandle(hConsumer);
    CloseHandle(hProducerEvent);
    CloseHandle(hConsumerEvent);
    DeleteCriticalSection(&cs);
    
    return 0;
}

可等待定时器

CreateWaitableTimer

C++
1
2
3
4
5
HANDLE CreateWaitableTimer(
    LPSECURITY_ATTRIBUTES lpTimerAttributes,
    BOOL                  bManualReset,
    LPCTSTR               lpTimerName
);

SetWaitableTimer

C++
1
2
3
4
5
6
7
8
BOOL SetWaitableTimer(
    HANDLE          hTimer,
    const LARGE_INTEGER *lpDueTime,
    LONG            lPeriod,
    PTIMERAPCROUTINE pfnCompletionRoutine,
    LPVOID          lpArgToCompletionRoutine,
    BOOL            fResume
);

示例

C++
#include <windows.h>
#include <stdio.h>

int main() {
    HANDLE hTimer = CreateWaitableTimer(NULL, FALSE, NULL);
    
    // 设置定时器:1秒后触发,每2秒一次
    LARGE_INTEGER dueTime;
    dueTime.QuadPart = -10000000LL;  // 1秒(单位:100纳秒)
    
    SetWaitableTimer(hTimer, &dueTime, 2000, NULL, NULL, FALSE);
    
    for (int i = 0; i < 5; i++) {
        WaitForSingleObject(hTimer, INFINITE);
        printf("Timer triggered: %d\n", i + 1);
    }
    
    CancelWaitableTimer(hTimer);
    CloseHandle(hTimer);
    
    return 0;
}

CancelWaitableTimer

C++
1
2
3
BOOL CancelWaitableTimer(
    HANDLE hTimer
);

互锁变量操作

原子操作,不需要额外同步。

InterlockedIncrement

C++
1
2
3
LONG InterlockedIncrement(
    LONG *Addend
);

InterlockedDecrement

C++
1
2
3
LONG InterlockedDecrement(
    LONG *Addend
);

InterlockedExchange

C++
1
2
3
4
LONG InterlockedExchange(
    LONG *Target,
    LONG Value
);

InterlockedCompareExchange

C++
1
2
3
4
5
LONG InterlockedCompareExchange(
    LONG *Destination,
    LONG  Exchange,
    LONG  Comparand
);

示例

C++
#include <windows.h>
#include <stdio.h>

LONG g_counter = 0;

DWORD WINAPI ThreadFunc(LPVOID param) {
    for (int i = 0; i < 10000; i++) {
        InterlockedIncrement(&g_counter);
    }
    return 0;
}

int main() {
    HANDLE threads[10];
    for (int i = 0; i < 10; i++) {
        threads[i] = CreateThread(NULL, 0, ThreadFunc, NULL, 0, NULL);
    }
    
    WaitForMultipleObjects(10, threads, TRUE, INFINITE);
    
    printf("Counter: %d\n", g_counter);
    
    for (int i = 0; i < 10; i++) {
        CloseHandle(threads[i]);
    }
    
    return 0;
}

参考资料