本笔记整理自:《Windows核心编程(第五版)》
目录
- 什么是线程同步
- 用户方式中的线程同步
- 原子访问:Interlocked系列函数
- CRITICAL_SECTION:关键段
- 内核对象的同步方式
- 事件内核对象
- 可等待的计时器内核对象
- 信号量内核对象
- 互斥量内核对象
InterlockedIncrement(LONG volatile *Addend) // *Addend++; InterlockedDecrement(LONG volatile *Addend) // *Addend--; InterlockedExchangeAdd(LONG volatile *Addend,LONG Value) // *Addend+=Value; InterlockedExchangeSubtract(LONG volatile *Addend,LONG Value) // *Addend-=Value; InterlockedExchange(LONG volatile *Target,LONG Value) // *Target=Value; TInterlockedExchangePointer(PVOID volatile *Target,PVOID Value) // *Target=&Value; InterlockedCompareExchange(LONG volatile *Target,LONG Exchange,Long Compared) // if(*Target==Compared) *pDest=Exchange; InterlockedCompareExchangePointer(PVOID volatile *Target,PVOID Exchange,PVOID Compared) // if(*pDest==pCompare) pDest=&value;
//Samples: CRITICAL_SECTION g_cs; InitializeCriticalSection(&g_cs); void thread_enter_function() { EnterCriticalSection(&g_cs); //访问线程共享的变量 //在此范围内,涉及到的数据只允许一个线程使用 //To-DO:... LeaveCriticalSection(&g_cs); }
//初始化 VOID InitializeCriticalSection(PCRITICAL_SECTION* pcs); //删除变量。当不需要这一结构体时,就可以调用此方法删除此变量 VOID DeleteCriticalSection(PCRITICAL_SECTION* pcs); // 是否允许访问,可以用此函数代替EnterCriticalSection // 每一个返回true的TryEnterCriticalSection的调用必须搭配一个LeaveCriticalSection // 非挂起式关键段访问 // 若有其他线程访问此关键段,则返回FALSE。可以访问则放回TRUE BOOL TryEnterCriticalSection(PCRITICAL_SECTION pcs); //进入关键段(当有其他在访问时会挂起) VOID EnterCriticalSection(PCRITICAL_SECTION pcs); //离开关键段 VOID LeaveCriticalSection(PCRITICAL_SECTION pcs); //设置挂起前试图访问锁的次数 //也就是说:若当前有其他地方正在访问关键段时,此处持续访问的次数,若超过这一次数,此处将会挂起。 SetCriticalSectionSpinCount(PCRITICAL_SECTION pcs,DWORD dwSpinCount); //设置挂起前试图访问锁的次数并初始化变量 InitializeCriticalSectionAndSpinCount(PCRITICAL_SECTION pcs,DWORD dwSpinCount);
/* * @params: * hObject:要等待的内核对象 * dwMilliseconds:线程最多愿意花多长的时间来等待对象被触发。可以设为INFINITE来表示无限长的时间 * * @return: * 指定了当前的状态 * WAIT_OBJECT_0 : 表示等待的对象被触发 * WAIT_TIMEOUT : 表示等待对象超过了dwMilliseconds * WAIT_FAILED : 给WaitForSingleObject传入了无效参数 */ DWORD WaitForSingleObject(HANDLE hObject,DWORD dwMilliseconds); /* * 和WaitForSingleObject类似,区别在于此函数可以同时检查多个内核对象的触发情况 * * @params: * dwCount : 希望函数检查内核对象的数量,范围是 [1 , MAXIMUM_WAIT_OBJECTS] * phObjects : 内核对象数组 * bWaitAll : 是否等待所有内核对象触发才取消堵塞(为false时只要有一个触发就取消堵塞) * dwMilliseconds:线程最多愿意花多长的时间来等待对象被触发。可以设为INFINITE来表示无限长的时间 * * @return * 和WaitForSingleObject的区别在于: * WAIT_OBJECT_0 : 表示等待的对象被触发1个 * WAIT_OBJECT_1 : 表示等待的对象被触发2个 * ... * bWaitAll设为false,正常情况下返回[ 1 , WAIT_OBJECT_0+(dwCount-1) ] * * PS:如果bWaitAll设为true,则返回WAIT_OBJECT_0 */ DWORD WaitForMultipleObjects(DWORD dwCount,CONST HANDLE* phObjects,BOOL bWaitAll,DWORD dwMilliseconds);
基于此等待函数,下面将介绍四种内核对象。
/* * @params * psa:安全性结构体 * bManualReset : 创建的是一个手动事件(TRUE)还是自动重置事件(FALSE) * bInitialState : 初始的状态是触发(TRUE)还是未触发(FALSE) * pszName:事件名称(唯一标识字符串) */ HANDLE CreateEvent(PSECURITY_ATTRIBUTES psa,BOOL bManualReset,BOOL bInitialState,PCTSTR pszName); /* * 打开已存在的事件。(供其他线程访问此事件) * @params * dwDesireAccess:(int)指定对事件对象的请求访问权限,如果安全描述符指定的对象不允许要求通过对调用该函数的过程,函数将返回失败 * hInheriy:是否继承 * pszName:事件名称 */ HANDLE OpenEvent(DWORD dwDesireAccess,BOOL hInheriy,PCTSTR pszName); BOOL SetEvent(HANDLE hEvent); //把事件设置为触发状态 BOOL ResetEvent(HANDLE hEvent); //把事件设置为未触发状态 BOOL PulseEvent(HANDLE hEvent); //触发一次或设置为未触发,相当于激发一次。(不常用)
在某个事件或按规定的间隔事件发出自己的信号通知的内核对象。
/* * @params: * bManualReset:是手动重置计时器还是自动重置计时器 */ HANDLE CreateWaitableTimer(PSECURITY_ATTRIBUTES psa,BOOL bManualReset,PCTSTR pszName) //打开已存在的计时器内核对象 HANDLE OpenWaitableTimer(PSECURITY_ATTRIBUTES psa,BOOL bManualReset,PCTSTR pszName) /* * @params * hTimer : 想要触发的计时器 * pDueTime : 第一次触发的事件的时间 * lPeriod :在第一次触发之后,计时器应该以怎样的频度触发。(单位为毫秒) * pfnCompletionRoutine : 可设为NULL * pvArgToCompletionRoutine : 可设为NULL * bResume : 可设为false */ BOOL SetWaitableTimer( HANDLE hTimer, const LARGE_INTEGER *pDueTime; LONG lPeriod, PTIMERAPCROUTINE pfnCompletionRoutine, PVOID pvArgToCompletionRoutine, BOOL bResume ); //取消计时器 BOOL CancelWaitableTimer(HANDLE hTimer);
HANDLE CreateSemaphore(PSECURITY_ATTRIBUTES psa,LONG lInitialCount,LONG lMaximumCount,PCTSTR pszName); HANDLE OpenSemaphore(DWORD dwDesiredAccess,BOOL bInheritHandle,PCTSTR pszName); /* * @params: * hSemaphore : 信号量内核对象句柄 * lReleaseCount : 增加的计数值 * plPreviousCount : 增加前的计数值 */ BOOL ReleaseSemaphore(HANDLE hSemaphore,LONG lReleaseCount,PLONG plPreviousCount)
特征 | 互斥量 | 关键段 |
---|---|---|
性能 | 慢 | 快 |
是否能跨进程使用 | 是 | 否 |
声明 | HANDLE hmtx; | CRITICAL_SECTION cs; |
初始化方式 | hmtx=CreateMutex(NULL,FALSE,NULL); | InitializeCriticalSection(&cs); |
清理 | CloseHandle(hmtx); | DeleteCriticalSection(&cs); |
无限等待 | WaitForSingleObject (hmtx, INFINITE); | EnterCriticalSection(&cs); |
0等待 | WaitForSingleObject (hmtx, 0); | TryEnterCriticalSection(&cs); |
任意长时间的等待 | WaitForSingleObject (hmtx, timeLength) | 不支持 |
释放 | ReleaseMutex(hmtx); | LeaveCriticalSection(&cs); |
是否能同时等待其他内核对象 | 是(WaitForMultipleObjects或其他) | 否 |
/* * @params * bInitialOwner : 控制互斥量的初始化状态, * FALSE :无占用 * TRUE:占用的线程为当前线程 */ HANDLE CreateMutex( PSECURITY_ATTRIBUTES psa; BOOL bInitialOwner, PCTSTR pszName ); HANDLE OpenMutex( DWORD dwDesiredAccess, BOOL bInitialOwner, PCTSTR pszName ); BOOL ReleaseMutex(HANDLE hMutex);