线程同步方法

一 windows下的方法

1 ring3层同步方法

  <1> 原子访问系列函数(优点是相对于其他的方案, 执行效率高)

    (1)原子加减法

       LONG __cdecl InterlockedExchangeAdd(LONG volatile*Addend,LONG Value);  

      LONGLONG __cdecl InterlockedExchangeAdd64(LONGLONG volatile*Addend,LONGLONG  Value);  

      分别针对32位/64位有符号整数进行原子加法的操作, 当Value为负数时, 做原子减法,返回初始值

    (2) 原子替换

      LONG  __cdecl InterlockedExchange(LONG volatile *Target,LONG Value);//32位

      SHORT __cdecl InterlockedExchange16(SHORT volatile *Target,SHORT Value);//16位

      LONGLONG  __cdecl InterlockedExchange64(LONG  LONG volatile *Target,LONGLONG Value);//64位

      PVOID __cdecl InterlockedExchangePointer(PVOID volatile *Target,PVOID Value); //这个本意是用来替换指针变量的, 其实也可以用来替换普通的变量, 此时PVOID可以理解成VOID

      把第一个参数的指向的值替换为第二个参数, 并返回替换之前的值, 其中最后一个是自适应的函数, 在32位系统下替换32位值, 64位替换64位值, 返回的是替换之前的值

    (3)原子比较替换

      LONG __cdecl InterlockedCompareExchange(LONG volatile *Destination, LONG Exchange,LONG Comparand);

      LONGLONG  __cdecl InterlockedCompareExchange64(LONGLONG  volatile *Destination, LONGLONG  Exchange,LONGLONG  Comparand);

      PVOID __cdecl InterlockedCompareExchangePointer(PVOID volatile *Destination, PVOID Exchange,PVOID Comparand);

      先比较再替换, 如果不同则不替换

 

  <2> 使用关键段(优点: 内部会使用1中的函数, 运行效率比较高; 缺点: 不能跨进程使用)

    (1)初始化函数

    CRITICAL_SECTION cs;

    void __stdcall InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection); 

    BOOL WINAPI InitializeCriticalSectionAndSpinCount(LPCRITICAL_SECTION lpCriticalSection,  DWORD dwSpinCount); 

    两个的区别是第二个在使用关键段的同时也使用自旋锁, 当某个要进入临界区的线程被判定为需要等待时, 先使用自旋锁方式进行等待, 然后再进入等待状态.

    使用场景是如果等待的时间比较短, 这种效率会更高,因为使线程进入等待状态需要使用大约1000个cpu周期. 第二个参数一般设置为4000以下

    (2)进入离开临界区

    void WINAPI EnterCriticalSection(_Inout_ LPCRITICAL_SECTION lpCriticalSection);

    void WINAPI LeaveCriticalSection(_Inout_ LPCRITICAL_SECTION lpCriticalSection);

    BOOL WINAPI TryEnterCriticalSection(  _Inout_ LPCRITICAL_SECTION lpCriticalSection);

    // 这个跟EnterCriticalSection类似, 区别就是这个如果不能进入临界区, 则返回FALSE, 不会卡在这;

    //返回TRUE的时候可以进入临界区, 之后必须调用LeaveCriticalSection

    (3)删除结构体

    void WINAPI DeleteCriticalSection(_Inout_ LPCRITICAL_SECTION lpCriticalSection);

 

  <3> Slim 读写锁(不能递归的获得SRWLock)

  居然有一个专门的读写锁, %>_<%, 之前一直以为需要为这种需求专门造轮子

    (1) 初始化

    SRWLOCK ss;

    VOID WINAPI InitializeSRWLock(_Out_ PSRWLOCK SRWLock); 

    (2)写入线程

    VOID WINAPI AcquireSRWLockExclusive(  _Inout_ PSRWLOCK SRWLock);

    VOID WINAPI ReleaseSRWLockExclusive(  _Inout_ PSRWLOCK SRWLock);

    (3)读取线程

    VOID WINAPI AcquireSRWLockShared(  _Inout_ PSRWLOCK SRWLock);

    VOID WINAPI ReleaseSRWLockShared(  _Inout_ PSRWLOCK SRWLock); 

    (4)没有销毁函数

 

  <4> 条件变量(这个主要是跟上面的2,3配套使用的, 不能单独使用)

    (1)初始化

     CONDITION_VARIABLE cc;

     VOID WINAPI InitializeConditionVariable( _Out_ PCONDITION_VARIABLE ConditionVariable);

    (2)等待函数(在进入临界区后调用, 表示等待条件的发生)

    BOOL WINAPI SleepConditionVariableCS(PCONDITION_VARIABLE ConditionVariable, PCRITICAL_SECTION CriticalSection, DWORD dwMilliseconds);

    BOOL WINAPI SleepConditionVariableSRW(_Inout_ PCONDITION_VARIABLE ConditionVariable,  _Inout_ PSRWLOCK SRWLock,  _In_    DWORD dwMilliseconds,  _In_ ULONG Flags);

    (3)唤醒条件函数

    VOID WINAPI WakeConditionVariable( _Inout_ PCONDITION_VARIABLE ConditionVariable);

    VOID WINAPI WakeAllConditionVariable( _Inout_ PCONDITION_VARIABLE ConditionVariable);

2 利用内核对象进行线程同步

  <0> 一般非匿名内核对象的名称默认是放在进程所在的会话空间的, 当在名字前面"Global"时, 就会放在全局命名空间中, 此外还有, 服务的内核对象始终位于全局命名空间中.

  <1> 内核对象的等待函数

    内核对象有两种状态, 激发态跟非激发态, 等待函数等待的就是内核对象变成激发态

    当等待的是自动重置事件时, 等待成功后会自动设置为非激发态

    DWORD WINAPI WaitForSingleObject(HANDLE hHandle,DWORD  dwMilliseconds);

    DWORD WINAPI SignalObjectAndWait(HANDLE hObjectToSignal,HANDLE hObjectToWaitOn,DWORD  dwMilliseconds,BOOL   bAlertable); //  第一个参数只能是semaphore, a mutex, or an event

    DWORD WINAPI WaitForMultipleObjects(DWORD  nCount,const HANDLE *lpHandles,BOOL   bWaitAll,DWORD  dwMilliseconds);// 这个等待函数执行的是原子操作, 当等待一个以上的对象时, 且这些对象为自动重置事件时, 会全部以原子方式设置为非激发态

    等待函数返回值:

      WAIT_OBJECT_0 to ( WAIT_OBJECT_0 + nCount– 1) : 等待的对象变成了激发态, 如果是等待多个对象, 返回的是在等待对象数组的Index+ WAIT_OBJECT_0

      WAIT_TIMEOUT: 等待超时

      WAIT_FAILED: 等待参数有问题

  <2> 事件(Event)  

    HANDLE WINAPI CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttributes,BOOL bManualReset,BOOL bInitialState,LPCTSTR lpName); //创建事件

    HANDLE WINAPI CreateEventEx(LPSECURITY_ATTRIBUTES lpEventAttributes,LPCTSTR lpName,DWORD dwFlags,DWORD dwDesiredAccess);

    BOOL WINAPI SetEvent(_In_ HANDLE hEvent);

    BOOL WINAPI ResetEvent(  _In_ HANDLE hEvent);

    BOOL WINAPI PulseEvent(  _In_ HANDLE hEvent); //不常用, 相当于先调用SetEvent,然后立即调用ResetEvent

    CloseHandle() // 关闭事件对象

  <3>可等待的计时器内核对象   

HANDLE WINAPI CreateWaitableTimer(//该种内核对象,创建后总是未触发状态
  _In_opt_ LPSECURITY_ATTRIBUTES lpTimerAttributes,
  _In_     BOOL                  bManualReset,
  _In_opt_ LPCTSTR               lpTimerName
);
BOOL WINAPI SetWaitableTimer(//创建完成后, 调用该函数设置相关属性
  _In_           HANDLE           hTimer,
  _In_     const LARGE_INTEGER    *pDueTime, //首次触发时间
  _In_           LONG             lPeriod,//之后的触发频率, 如果设置为0, 表示该计时器内核对象只触发一次
  _In_opt_       PTIMERAPCROUTINE pfnCompletionRoutine,
  _In_opt_       LPVOID           lpArgToCompletionRoutine,
  _In_           BOOL             fResume
);

相关的时间转换函数: 
  SystemTimeToFileTime: 本地系统时间转换成文件时间,
  LocalFileTimeToFileTime: 本地文件时间转换成 UTC文件时间

BOOL WINAPI CancelWaitableTimer(// 取消该内核对象的周期性触发动作, 但不改变内核对象的当前状态
  _In_ HANDLE hTimer
);

  <2>信号量(Semaphore) 

HANDLE WINAPI CreateSemaphore(
  _In_opt_ LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
  _In_     LONG                  lInitialCount,
  _In_     LONG                  lMaximumCount,
  _In_opt_ LPCTSTR               lpName
);
HANDLE WINAPI CreateSemaphoreEx(
  _In_opt_   LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
  _In_       LONG                  lInitialCount,
  _In_       LONG                  lMaximumCount,
  _In_opt_   LPCTSTR               lpName,
  _Reserved_ DWORD                 dwFlags, //系统保留, 设为0
  _In_       DWORD                 dwDesiredAccess
);
HANDLE WINAPI OpenSemaphore(
  _In_ DWORD   dwDesiredAccess,
  _In_ BOOL    bInheritHandle,
  _In_ LPCTSTR lpName
);

BOOL WINAPI ReleaseSemaphore(
  _In_      HANDLE hSemaphore,
  _In_      LONG   lReleaseCount,//不能传0,或者超过信号量中资源的上限值
  _Out_opt_ LPLONG lpPreviousCount
);
CloseHandle() // 关闭信号量对象

<3>互斥量(mutex)
如果占用线程在ralease之前被强行关闭了, 那么其他等待的线程的等待函数会返回 WAIT_ABANDONED
HANDLE WINAPI CreateMutex(
  _In_opt_ LPSECURITY_ATTRIBUTES lpMutexAttributes,
  _In_     BOOL                  bInitialOwner,// TRUE, 表示该线程已经把这个对象占用了, 所以是未触发状态, 否则是触发状态
  _In_opt_ LPCTSTR               lpName
);
HANDLE WINAPI CreateMutexEx(
  _In_opt_ LPSECURITY_ATTRIBUTES lpMutexAttributes,
  _In_opt_ LPCTSTR               lpName,
  _In_     DWORD                 dwFlags,
  _In_     DWORD                 dwDesiredAccess
);
HANDLE WINAPI OpenMutex(
  _In_ DWORD   dwDesiredAccess,
  _In_ BOOL    bInheritHandle,
  _In_ LPCTSTR lpName
);
BOOL WINAPI ReleaseMutex(
  _In_ HANDLE hMutex
);



    

    

原文地址:https://www.cnblogs.com/talenth/p/8727462.html