线程同步的八种利用方法小结
字数 1165 2025-08-07 08:21:50
Windows线程同步机制详解
一、线程同步概述
线程同步是指当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作。线程同步的本质是线程之间的协调、协同和互相配合完成工作。
Windows提供了多种线程同步机制,主要分为两大类:
- 用户模式同步:原子操作、关键段(临界区)、读写锁
- 内核模式同步:事件、可等待计时器、信号量、互斥量
二、等待函数
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
);
十一、性能比较
- 用户模式同步(原子操作、关键段、读写锁)性能高于内核模式同步
- 读写锁在读写分离场景下性能最高
- 原子操作最简单高效,但功能有限
- 内核模式同步(事件、计时器、信号量、互斥量)适用于跨进程场景
十二、选择指南
- 单进程内同步优先使用用户模式同步
- 读写分离场景使用读写锁
- 简单计数器使用原子操作
- 需要跨进程同步时使用内核模式同步
- 定时任务使用可等待计时器
- 资源计数控制使用信号量
- 互斥访问使用互斥量或关键段