关键段结构
typedef struct _RTL_CRITICAL_SECTION {
PRTL_CRITICAL_SECTION_DEBUGDebugInfo;
LONG LockCount;
LONG RecursionCount;
HANDLE OwningThread; // 这个最关键,理解了这个变量就理解了关键段
HANDLE LockSemaphore;
DWORD SpinCount;
} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;
关键段中有个线程所有权的概念,
简单来说就是调用 EnterCriticalSection 的线程, 自身必须调用 LeaveCriticalSection 才会释放. Enter 与Leave 要配对使用.
如若在别的线程调用 LeaveCriticalSection 是无效的;
同时需要注意 EnterCriticalSection 可以在同一线程中调用 RecursionCount 次 , 那么 LeaveCriticalSection 也需要调用相同次数.
否则死锁;
注意:
关键段与读写锁 都是用于线程间的互斥, 但无法用于线程同步 , 线程同步需要用到内核对象,比如event对象.
比如关键段只能在同一线程中使用Enter , Leave. 而无法在一个线程中使用Enter , 另一个使用Leave;
代码说明:
int g_count = 0;
//2个关键段
CRITICAL_SECTION cs_lock_param,cs_lock_thread;
unsigned int WINAPI t1( void* lpParameter)
{
// 应在同一线程中调用 Enter , Leave
EnterCriticalSection(&cs_lock_param);
int param = *(int*)lpParameter;
LeaveCriticalSection(&cs_lock_param);
EnterCriticalSection(&cs_lock_thread);
// EnterCriticalSection(&cs_lock_thread); 此处取消注释将死锁.Enter , Leave 注意配对使用
g_count++;
cout <<"tid:" << GetCurrentThreadId() << "," <<g_count << "," << param<< endl;
LeaveCriticalSection(&cs_lock_thread);
return 0;
}
int main()
{
/*
初始化关键段, 也可以使用InitializeCriticalSection
InitializeCriticalSectionAndSpinCount 在多核处理器上或许速度更快些,使用旋转锁
先循环尝试进入,如果超时, 则切换成内核等待状态
*/
InitializeCriticalSectionAndSpinCount(&cs_lock_param,4000); // 4000 是循环次数.
InitializeCriticalSectionAndSpinCount(&cs_lock_thread,4000);
HANDLE handles[5];
for(int i = 0; i < 5; ++i) {
/*
注意不能在这里EnterCriticalSection;
即在另一个线程中 Leave 是无效的
*/
//EnterCriticalSection(&cs_lock_param);
handles[i] = (HANDLE)_beginthreadex(NULL,0,t1,&i,0,NULL);
}
WaitForMultipleObjects(5,handles,TRUE,INFINITE);
DeleteCriticalSection(&cs_lock_param);
DeleteCriticalSection(&cs_lock_thread);
for(int i = 0; i < 5 ; ++i)
CloseHandle(handles[i]);
return 0;
}
读写锁:
读锁: 多个线程可同时访问.
AcquireSRWLockShared 可同时进入.
对应的读线程很适合用于读取共享资源
写锁:与关键段类似,如果愿意, 你也可以把关键段替换成 写锁 ,
<