参考博客:http://blog.csdn.net/morewindows/article/details/7442639
本篇用CRITICAL_SECTION来解决多线程同步互斥的问题
CRITICAL_SECTION 一共有四个函数:
1.初始化:定义关键段变量后必须先进行初始化才能使用
void InitializeCriticalSection(LPCRITICAL_SECTIONlpCriticalSection)
2.销毁:使用完之后要记得销毁
void DeleteCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);
3.进入关键区域:保证各个线程互斥的进入关键区域
void EnterCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);
4.离开关键区域:
void LeaveCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);
1 #include <stdio.h> 2 #include <process.h> 3 #include <windows.h> 4 //关键段的定义 5 CRITICAL_SECTION g_csThreadParameter, g_csThreadCode; 6 long g_num; //登录次数 7 unsigned int __stdcall Fun(void *pPM); //线程函数 8 const DWORD THREAD_NUM = 10;//启动线程数 9 unsigned int __stdcall Fun(void *pPM) 10 { 11 //由于创建线程是要一定的开销的,所以新线程并不能第一时间执行到这来 12 int nThreadNum = *(int *)pPM; //子线程获取参数 13 LeaveCriticalSection(&g_csThreadParameter); 14 Sleep(50);//some work should to do 15 EnterCriticalSection(&g_csThreadCode);//进入子线程序号的关键区域 16 g_num++; //处理全局资源 17 // InterlockedIncrement(&g_num); 18 Sleep(0);//some work should to do 19 printf("线程编号为%d 全局资源值为%d\n", nThreadNum, g_num); 20 LeaveCriticalSection(&g_csThreadCode); 21 return 0; 22 } 23 int main() 24 { 25 printf(" 关键段CS的演示\n"); 26 g_num = 0; 27 28 //关键段的初始化 29 InitializeCriticalSection(&g_csThreadParameter); 30 InitializeCriticalSection(&g_csThreadCode); 31 HANDLE handle[THREAD_NUM]; 32 int i=0; 33 while (i<THREAD_NUM) 34 { 35 EnterCriticalSection(&g_csThreadParameter);//进入子线程序号的关键区域 36 handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL); 37 i++; 38 } 39 40 WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE); 41 //离开关键区域 42 LeaveCriticalSection(&g_csThreadParameter); 43 LeaveCriticalSection(&g_csThreadCode); 44 return 0; 45 }
运行结果:
结果可以看出子线程已经互斥访问资源 ,但子线程间的同步还是出现了问题
下面分析为什么不能实现线程间的同步
关键段结构体CRITICAL_SECTION的定义:
typedef struct _RTL_CRITICAL_SECTION {
PRTL_CRITICAL_SECTION_DEBUGDebugInfo;
LONGLockCount;
LONGRecursionCount;
HANDLEOwningThread; // from the thread's ClientId->UniqueThread
HANDLELockSemaphore;
DWORDSpinCount;
} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;
第一个参数:调试时使用
第二个参数:初始化为-1,表示有多少个线程在等待
第三个参数:表示该关键段的拥有者获得该关键段的次数,初始化为0
第四个参数:拥有该关键段的线程句柄
第五个参数:是一个自复位事件
第六个参数:旋转锁的设置,单核CPU无意义
关键段会有线程拥有权的概念第四个参数OwningThread来批准进入关键段的线程
EnterCriticalSection()会更新第四个参数,并立即返回让该线程进入其他线程再次调用EnterCriticalSection()则会被切换到等待状态
一旦拥有所有权的线程调用LeaveCriticalSection()使其进入次数为零时,系统会自动更新关键段,并将等待中的线程切换到可调度状态
所以关键段用于进程间的互斥,但不能用于进程间的同步
由于线程切换到等待状态开销较大,为了提高性能,微软将旋转锁合并到关键段中:EnterCriticalSection()会先用一个旋转锁不断循环,尝试一段时间后
再将线程进入循环状态
1.函数InitializeCriticalSectionAndSpinCount()初始化关键段,并设置旋转锁次数,一般设置为4000次
BOOLInitializeCriticalSectionAndSpinCount(
LPCRITICAL_SECTIONlpCriticalSection,
DWORDdwSpinCount);
2.修改关键段的旋转次数SetCriticalSectionSpinCount( )
DWORDSetCriticalSectionSpinCount(
LPCRITICAL_SECTIONlpCriticalSection,
DWORDdwSpinCount);
《Windows核心编程》第五版的第八章推荐在使用关键段的时候同时使用旋转锁,这样有助于提高性能。值得注意的是如果主机只有一个处理器,那么设置旋转锁是无效的。无法进入关键区域的线程总会被系统将其切换到等待状态。
最后总结下关键段:
1.关键段共初始化化、销毁、进入和离开关键区域四个函数。
2.关键段可以解决线程的互斥问题,但因为具有“线程所有权”,所以无法解决同步问题。
3.推荐关键段与旋转锁配合使用