线程同步的八种利用方法小结
字数 1165 2025-08-07 08:21:50

Windows线程同步机制详解

一、线程同步概述

线程同步是指当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作。线程同步的本质是线程之间的协调、协同和互相配合完成工作。

Windows提供了多种线程同步机制,主要分为两大类:

  1. 用户模式同步:原子操作、关键段(临界区)、读写锁
  2. 内核模式同步:事件、可等待计时器、信号量、互斥量

二、等待函数

1. 等待单个对象

DWORD WaitForSingleObject(
    HANDLE hHandle,      // 对象句柄
    DWORD dwMilliseconds // 等待时间(毫秒)INFINITE表示无限等待
);

返回值:

  • WAIT_ABANDONED (0x00000080):当hHandle为mutex时,拥有mutex的线程在结束时没有释放内核对象
  • WAIT_OBJECT_0 (0x00000000):对象变为有信号状态
  • WAIT_TIMEOUT (0x00000102):等待超时
  • WAIT_FAILED (0xFFFFFFFF):出现错误

2. 等待多个对象

DWORD WaitForMultipleObjects(
    DWORD dwCount,        // 等待对象数量(最多64)
    CONST HANDLE* phObjects, // 对象句柄数组
    BOOL fWaitAll,        // TRUE:等待全部对象,FALSE:等待任意一个对象
    DWORD dwMilliseconds  // 等待时间
);

当fWaitAll为TRUE时,所有对象变为已通知状态则返回WAIT_OBJECT_0;为FALSE时,返回触发对象的索引值。

三、原子操作

原子操作是最基础的同步机制,保证操作的不可分割性。

1. 基本原子操作

// 递增
LONG InterlockedIncrement(LONG volatile* Addend);
LONGLONG InterlockedIncrement64(LONGLONG volatile* Addend);

// 递减
LONG InterlockedDecrement(LONG volatile* Addend);
LONGLONG InterlockedDecrement64(LONGLONG volatile* Addend);

// 加法
LONG InterlockedExchangeAdd(LONG volatile* Addend, LONG Value);
LONGLONG InterlockedExchangeAdd64(LONGLONG volatile* Addend, LONGLONG Value);

// 赋值
LONG InterlockedExchange(LONG volatile* Addend, LONG Value);
LONGLONG InterlockedExchange64(LONGLONG volatile* Addend, LONGLONG Value);
PVOID InterlockedExchangePointer(PVOID volatile *Target, PVOID Value);

// 比较并交换
LONG InterlockedCompareExchange(LONG* Destination, LONG ExChange, LONG Comperand);
LONGLONG InterlockedCompareExchange64(LONGLONG volatile *Destination, LONGLONG Exchange, LONGLONG Comparand);
PVOID InterlockedCompareExchangePointer(PVOID *Destination, PVOID Exchange, PVOID Comparand);

2. 位运算原子操作

// 与运算
SHORT InterlockedAnd16(SHORT volatile *Destination, SHORT Value);
LONG InterlockedAnd(LONG volatile *Destination, LONG Value);
LONGLONG InterlockedAnd64(LONGLONG volatile *Destination, LONGLONG Value);

// 或运算
SHORT InterlockedOr16(SHORT volatile *Destination, SHORT Value);
LONG InterlockedOr(LONG volatile *Destination, LONG Value);
LONGLONG InterlockedOr64(LONGLONG volatile *Destination, LONGLONG Value);

// 异或运算
SHORT InterlockedXor16(SHORT volatile *Destination, SHORT Value);
LONG InterlockedXor(LONG volatile *Destination, LONG Value);
LONGLONG InterlockedXor64(LONGLONG volatile *Destination, LONGLONG Value);

四、关键段(临界区)

关键段是用户模式的同步机制,适用于单进程内的线程同步。

1. 基本操作

// 定义关键段
CRITICAL_SECTION cs;

// 初始化
InitializeCriticalSection(&cs);
InitializeCriticalSectionEx(&cs, dwSpinCount, Flags);
InitializeCriticalSectionAndSpinCount(&cs, dwSpinCount);

// 进入关键段
EnterCriticalSection(&cs);

// 尝试进入关键段
BOOL TryEnterCriticalSection(&cs);

// 离开关键段
LeaveCriticalSection(&cs);

// 销毁关键段
DeleteCriticalSection(&cs);

2. 优化关键段

// 设置自旋计数
DWORD SetCriticalSectionSpinCount(
    LPCRITICAL_SECTION lpCriticalSection,
    DWORD dwSpinCount
);

自旋计数优化原理:

  • 在内核模式检测N次关键段是否可用
  • 超过检测次数才进入等待状态
  • 单核CPU无效

五、读写锁(SRWLock)

读写锁区分读操作和写操作,读操作可以共享,写操作独占。

1. 基本操作

// 定义读写锁
SRWLOCK sl;

// 初始化
InitializeSRWLock(&sl);

// 获取独占锁(写操作)
AcquireSRWLockExclusive(&sl);
ReleaseSRWLockExclusive(&sl);

// 获取共享锁(读操作)
AcquireSRWLockShared(&sl);
ReleaseSRWLockShared(&sl);

注意:读写锁没有销毁API

六、事件内核对象(Event)

事件对象是内核模式的同步机制,可以跨进程使用。

1. 创建和操作

// 创建事件
HANDLE CreateEvent(
    LPSECURITY_ATTRIBUTES lpEventAttributes,
    BOOL bManualReset,    // TRUE:手动重置,FALSE:自动重置
    BOOL bInitialState,   // TRUE:已触发,FALSE:未触发
    LPCTSTR lpName
);

HANDLE CreateEventEx(
    LPSECURITY_ATTRIBUTES lpEventAttributes,
    LPCTSTR lpName,
    DWORD dwFlags,        // CREATE_EVENT_MANUAL_RESET(1), CREATE_EVENT_INITIAL_SET(2)
    DWORD dwDesiredAccess
);

// 打开事件
HANDLE OpenEvent(
    DWORD dwDesiredAccess,
    BOOL bInheritHandle,
    LPCTSTR lpName
);

// 设置事件状态
BOOL SetEvent(HANDLE hEvent);     // 设置为已触发
BOOL ResetEvent(HANDLE hEvent);   // 设置为未触发

七、可等待计时器(WaitableTimer)

1. 创建和操作

// 创建计时器
HANDLE CreateWaitableTimer(
    LPSECURITY_ATTRIBUTES lpTimerAttributes,
    BOOL bManualReset,    // TRUE:手动重置,FALSE:自动重置
    LPCTSTR lpTimerName
);

HANDLE CreateWaitableTimerEx(
    LPSECURITY_ATTRIBUTES lpTimerAttributes,
    LPCTSTR lpTimerName,
    DWORD dwFlags,        // CREATE_WAITABLE_TIMER_MANUAL_RESET(1)
    DWORD dwDesiredAccess
);

// 打开计时器
HANDLE OpenWaitableTimer(
    DWORD dwDesiredAccess,
    BOOL bInheritHandle,
    LPCTSTR lpTimerName
);

// 设置计时器
BOOL SetWaitableTimer(
    HANDLE hTimer,
    const LARGE_INTEGER *pDueTime, // 首次触发时间(100ns单位)
    LONG lPeriod,         // 触发间隔(毫秒),0表示不重复
    PTIMERAPCROUTINE pfnCompletionRoutine,
    LPVOID lpArgToCompletionRoutine,
    BOOL fResume          // 是否唤醒系统
);

// 取消计时器
BOOL CancelWaitableTimer(HANDLE hTimer);

八、信号量(Semaphore)

信号量用于控制对共享资源的访问数量。

1. 创建和操作

// 创建信号量
HANDLE CreateSemaphore(
    LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
    LONG lInitialCount,   // 初始资源数
    LONG lMaximumCount,   // 最大资源数
    LPCTSTR lpName
);

HANDLE CreateSemaphoreEx(
    LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
    LONG lInitialCount,
    LONG lMaximumCount,
    LPCTSTR lpName,
    DWORD dwFlags,
    DWORD dwDesiredAccess
);

// 打开信号量
HANDLE OpenSemaphore(
    DWORD dwDesiredAccess,
    BOOL bInheritHandle,
    LPCTSTR lpName
);

// 释放信号量(增加资源)
BOOL ReleaseSemaphore(
    HANDLE hSemaphore,
    LONG lReleaseCount,   // 释放数量
    LPLONG lpPreviousCount // 之前可用数量
);

九、互斥量(Mutex)

互斥量用于确保同一时间只有一个线程访问共享资源。

1. 创建和操作

// 创建互斥量
HANDLE CreateMutex(
    LPSECURITY_ATTRIBUTES lpMutexAttributes,
    BOOL bInitialOwner,   // TRUE:当前线程拥有,FALSE:无归属
    LPCTSTR lpName
);

HANDLE CreateMutexEx(
    LPSECURITY_ATTRIBUTES lpMutexAttributes,
    LPCTSTR lpName,
    DWORD dwFlags,        // CREATE_MUTEX_INITIAL_OWNER(1)
    DWORD dwDesiredAccess
);

// 打开互斥量
HANDLE OpenMutex(
    DWORD dwDesiredAccess,
    BOOL bInheritHandle,
    LPCTSTR lpName
);

// 释放互斥量
BOOL ReleaseMutex(HANDLE hMutex);

2. 互斥量的特殊行为

  • 当拥有互斥量的线程意外终止时,系统会将互斥量标记为"遗弃"状态
  • 系统会自动将遗弃的互斥量重置为可用状态
  • 引用计数置为0,线程ID置为0

十、高级同步函数

1. 信号对象并等待

DWORD SignalObjectAndWait(
    HANDLE hObjectToSignal,   // 要触发的对象(互斥量/信号量/事件)
    HANDLE hObjectToWaitOn,   // 要等待的对象
    DWORD dwMilliseconds,
    BOOL bAlertable          // 是否处理APC
);

2. 消息等待

DWORD MsgWaitForMultipleObjects(
    DWORD nCount,
    const HANDLE *pHandles,
    BOOL bWaitAll,
    DWORD dwMilliseconds,
    DWORD dwWakeMask        // 等待的消息类型
);

DWORD MsgWaitForMultipleObjectsEx(
    DWORD nCount,
    const HANDLE *pHandles,
    DWORD dwMilliseconds,
    DWORD dwWakeMask,
    DWORD dwFlags
);

十一、性能比较

  1. 用户模式同步(原子操作、关键段、读写锁)性能高于内核模式同步
  2. 读写锁在读写分离场景下性能最高
  3. 原子操作最简单高效,但功能有限
  4. 内核模式同步(事件、计时器、信号量、互斥量)适用于跨进程场景

十二、选择指南

  1. 单进程内同步优先使用用户模式同步
  2. 读写分离场景使用读写锁
  3. 简单计数器使用原子操作
  4. 需要跨进程同步时使用内核模式同步
  5. 定时任务使用可等待计时器
  6. 资源计数控制使用信号量
  7. 互斥访问使用互斥量或关键段
Windows线程同步机制详解 一、线程同步概述 线程同步是指当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作。线程同步的本质是线程之间的协调、协同和互相配合完成工作。 Windows提供了多种线程同步机制,主要分为两大类: 用户模式同步:原子操作、关键段(临界区)、读写锁 内核模式同步:事件、可等待计时器、信号量、互斥量 二、等待函数 1. 等待单个对象 返回值: WAIT_ ABANDONED (0x00000080):当hHandle为mutex时,拥有mutex的线程在结束时没有释放内核对象 WAIT_ OBJECT_ 0 (0x00000000):对象变为有信号状态 WAIT_ TIMEOUT (0x00000102):等待超时 WAIT_ FAILED (0xFFFFFFFF):出现错误 2. 等待多个对象 当fWaitAll为TRUE时,所有对象变为已通知状态则返回WAIT_ OBJECT_ 0;为FALSE时,返回触发对象的索引值。 三、原子操作 原子操作是最基础的同步机制,保证操作的不可分割性。 1. 基本原子操作 2. 位运算原子操作 四、关键段(临界区) 关键段是用户模式的同步机制,适用于单进程内的线程同步。 1. 基本操作 2. 优化关键段 自旋计数优化原理: 在内核模式检测N次关键段是否可用 超过检测次数才进入等待状态 单核CPU无效 五、读写锁(SRWLock) 读写锁区分读操作和写操作,读操作可以共享,写操作独占。 1. 基本操作 注意:读写锁没有销毁API 六、事件内核对象(Event) 事件对象是内核模式的同步机制,可以跨进程使用。 1. 创建和操作 七、可等待计时器(WaitableTimer) 1. 创建和操作 八、信号量(Semaphore) 信号量用于控制对共享资源的访问数量。 1. 创建和操作 九、互斥量(Mutex) 互斥量用于确保同一时间只有一个线程访问共享资源。 1. 创建和操作 2. 互斥量的特殊行为 当拥有互斥量的线程意外终止时,系统会将互斥量标记为"遗弃"状态 系统会自动将遗弃的互斥量重置为可用状态 引用计数置为0,线程ID置为0 十、高级同步函数 1. 信号对象并等待 2. 消息等待 十一、性能比较 用户模式同步(原子操作、关键段、读写锁)性能高于内核模式同步 读写锁在读写分离场景下性能最高 原子操作最简单高效,但功能有限 内核模式同步(事件、计时器、信号量、互斥量)适用于跨进程场景 十二、选择指南 单进程内同步优先使用用户模式同步 读写分离场景使用读写锁 简单计数器使用原子操作 需要跨进程同步时使用内核模式同步 定时任务使用可等待计时器 资源计数控制使用信号量 互斥访问使用互斥量或关键段