一 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位
LONG LONG __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 利用内核对象进行线程同步
<-1> 一般非匿名内核对象的名称默认是放在进程所在的会话空间的, 当在名字前面"Global\"时, 就会放在全局命名空间中, 此外还有, 服务的内核对象始终位于全局命名空间中.
<0> 内核对象的等待函数
内核对象有两种状态, 激发态跟非激发态, 等待函数等待的就是内核对象变成激发态
当等待的是自动重置事件时, 等待成功后会自动设置为非激发态
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: 等待参数有问题
<1> 事件(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() // 关闭事件对象
<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 );