• 线程同步方法


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



        

        

  • 相关阅读:
    python 迭代器
    python 语言特性
    JavaScript 【语句】for-in语句
    Vue实例-创建第一个VUE应用
    【安装】Vue( 直接使用<script>方式引入、CDN方式引入 )
    到某个目录下新建文件
    NVM 安装( Windows)
    尾调用
    JavaScript 【函数表达式】私有变量
    JavaScript 【函数表达式】模仿块级作用域
  • 原文地址:https://www.cnblogs.com/talenth/p/8727462.html
Copyright © 2020-2023  润新知