本文共 3240 字,大约阅读时间需要 10 分钟。
,一般操作系统书上面都有。
适用范围:它只能同步一个进程中的线程,不能跨进程同步。一般用它来做单个进程内的代码快同步,效率比较高
windows中与临界区有关的结构是 CRITICAL_SECTION,关于该结构体的内部结构可参考
使用时,主线程中要先初始化临界区,最后要删除临界区,具体使用见下面代码:
从一个例子来说明:假设有三个线程都需要使用打印机,我们可以把打印的代码放到临界区,这样就可以保证每次只有一个线程在使用打印机。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | #include<string> #include<iostream> #include<process.h> #include<windows.h> using namespace std; //定义一个临界区 CRITICAL_SECTION g_cs; //线程绑定的函数返回值和参数是确定的,而且一定要__stdcall unsigned __stdcall threadFun( void *param) { EnterCriticalSection(&g_cs); //进入临界区,如果有其他线程则等待 cout<<*(string *)(param)<<endl; LeaveCriticalSection(&g_cs); //退出临界区,其他线程可以进来了 return 1; } int main() { //初始化临界区 InitializeCriticalSection(&g_cs); HANDLE hth1, hth2, hth3; string s1 = "first" , s2 = "second" , s3 = "third" ; //创建线程 hth1 = ( HANDLE )_beginthreadex(NULL, 0, threadFun, &s1, 0, NULL); hth2 = ( HANDLE )_beginthreadex(NULL, 0, threadFun, &s2, 0, NULL); hth3 = ( HANDLE )_beginthreadex(NULL, 0, threadFun, &s3, 0, NULL); //等待子线程结束 WaitForSingleObject(hth1, INFINITE); WaitForSingleObject(hth2, INFINITE); WaitForSingleObject(hth3, INFINITE); //一定要记得关闭线程句柄 CloseHandle(hth1); CloseHandle(hth2); CloseHandle(hth3); //删除临界区 DeleteCriticalSection(&g_cs); } |
再看另外一个问题:编写一个程序,开启3个线程,这3个线程的ID分别为A、B、C,每个线程将自己的ID在屏幕上打印10遍,要求输出结果必须按ABC的顺序显示;如:ABCABC….依次递推, 仿照文章中的代码,我们把信号量替换成临界区。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | #include<string> #include<iostream> #include<process.h> #include<windows.h> using namespace std; //声明3个临界区 CRITICAL_SECTION g_cs1, g_cs2, g_cs3; //线程绑定的函数返回值和参数是确定的,而且一定要__stdcall unsigned __stdcall threadFunA( void *) { for ( int i = 0; i < 10; i++){ EnterCriticalSection(&g_cs1); //进入临界区 cout<< "A" ; LeaveCriticalSection(&g_cs2); //离开临界区 } return 1; } unsigned __stdcall threadFunB( void *) { for ( int i = 0; i < 10; i++){ EnterCriticalSection(&g_cs2); //进入临界区 cout<< "B" ; LeaveCriticalSection(&g_cs3); //离开临界区 } return 2; } unsigned __stdcall threadFunC( void *) { for ( int i = 0; i < 10; i++){ EnterCriticalSection(&g_cs3); //进入临界区 cout<< "C" ; LeaveCriticalSection(&g_cs1); //离开临界区 } return 3; } int main() { //初始化临界区 InitializeCriticalSection(&g_cs1); InitializeCriticalSection(&g_cs2); InitializeCriticalSection(&g_cs3); HANDLE hth1, hth2, hth3; //创建线程 hth1 = ( HANDLE )_beginthreadex(NULL, 0, threadFunA, NULL, 0, NULL); hth2 = ( HANDLE )_beginthreadex(NULL, 0, threadFunB, NULL, 0, NULL); hth3 = ( HANDLE )_beginthreadex(NULL, 0, threadFunC, NULL, 0, NULL); //等待子线程结束 WaitForSingleObject(hth1, INFINITE); WaitForSingleObject(hth2, INFINITE); WaitForSingleObject(hth3, INFINITE); //一定要记得关闭线程句柄 CloseHandle(hth1); CloseHandle(hth2); CloseHandle(hth3); //删除临界区 DeleteCriticalSection(&g_cs1); DeleteCriticalSection(&g_cs2); DeleteCriticalSection(&g_cs3); } |
为什么会这样呢,因为临界区有所有权的概念,即某个线程进入临界区后,就拥有该临界区的所有权,在他离开临界区之前,他可以无限次的再次进入该临界区,上例中线程A获得临界区1的所有权后,在线程C调用LeaveCriticalSection(&g_cs1)之前,A是可以无限次的进入临界区1的。利用信号量之所以可以实现题目的要求,是因为信号量没有所有权的概念,某个线程获得信号量后,如果信号量的值为0,那么他一定要等到信号量被释放时,才能再次获得