系统相关
首页 > 系统相关> > Windows编程之线程同步

Windows编程之线程同步

作者:互联网

本笔记整理自:《Windows核心编程(第五版)》

目录

什么是线程同步

用户方式中的线程同步

原子访问:Interlocked系列函数

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;

CRITICAL_SECTION:关键段

//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);

标签:触发,HANDLE,Windows,编程,对象,线程,内核,BOOL
来源: https://www.cnblogs.com/ZhuSenlin/p/16663055.html