使用InitializeCriticalSection初始化关键代码段
使用EnterCriticalSection进入关键代码段:
使用LeaveCriticalSection离开关键代码段:
使用DeleteCriticalSection删除关键代码段,释放资源
我们看一个例子:
- #include <Windows.h>
- #include <stdio.h>
- //线程函数声明
- DWORD WINAPI Thread1Proc( LPVOID lpParameter);
- DWORD WINAPI Thread2Proc( LPVOID lpParameter);
- //全局变量
- int tickets = 100;
- CRITICAL_SECTION g_cs;
- int main()
- {
- HANDLE hThread1;
- HANDLE hThread2;
- //初始化关键代码段
- InitializeCriticalSection(&g_cs);
- //创建线程1
- hThread1 = CreateThread(NULL, //默认安全级别
- 0, //默认栈大小
- Thread1Proc,//线程函数
- NULL, //函数没有参数
- 0, //创建后直接运行
- NULL); //线程标识,不需要
- //创建线程2
- hThread2 = CreateThread(NULL, //默认安全级别
- 0, //默认栈大小
- Thread2Proc,//线程函数
- NULL, //函数没有参数
- 0, //创建后直接运行
- NULL); //线程标识,不需要
- //主线程休眠4秒
- Sleep(4000);
- //关闭线程句柄
- CloseHandle(hThread1);
- CloseHandle(hThread2);
- //关闭事件对象句柄
- DeleteCriticalSection(&g_cs);
- return 0;
- }
- //线程1入口函数
- DWORD WINAPI Thread1Proc( LPVOID lpParameter)
- {
- while(TRUE)
- {
- //进入关键代码段前调用该函数判断否能得到临界区的使用权
- EnterCriticalSection(&g_cs);
- Sleep(1);
- if(tickets > 0)
- {
- Sleep(1);
- printf("thread1 sell ticket : %d\n",tickets--);
- //访问结束后释放临界区对象的使用权
- LeaveCriticalSection(&g_cs);
- Sleep(1);
- }
- else
- {
- LeaveCriticalSection(&g_cs);
- break;
- }
- }
- return 0;
- }
- //线程2入口函数
- DWORD WINAPI Thread2Proc( LPVOID lpParameter)
- {
- while(TRUE)
- {
- //进入关键代码段前调用该函数判断否能得到临界区的使用权
- EnterCriticalSection(&g_cs);
- Sleep(1);
- if(tickets > 0)
- {
- Sleep(1);
- printf("thread2 sell ticket : %d\n",tickets--);
- //访问结束后释放临界区对象的使用权
- LeaveCriticalSection(&g_cs);
- Sleep(1);
- }
- else
- {
- LeaveCriticalSection(&g_cs);
- break;
- }
- }
- return 0;
- }
下面看一个多线程程序中常犯的一个错误-线程死锁。死锁产生的原因,举个例子:线程1拥有临界区资源A,正在等待临界区资源B;而线程2拥有临界区资源B,正在等待临界区资源A。它俩各不相让,结果谁也执行不了。我们看看程序:
- #include <Windows.h>
- #include <stdio.h>
- //线程函数声明
- DWORD WINAPI Thread1Proc( LPVOID lpParameter);
- DWORD WINAPI Thread2Proc( LPVOID lpParameter);
- //全局变量
- int tickets = 100;
- CRITICAL_SECTION g_csA;
- CRITICAL_SECTION g_csB;
- int main()
- {
- HANDLE hThread1;
- HANDLE hThread2;
- //初始化关键代码段
- InitializeCriticalSection(&g_csA);
- InitializeCriticalSection(&g_csB);
- //创建线程1
- hThread1 = CreateThread(NULL, //默认安全级别
- 0, //默认栈大小
- Thread1Proc,//线程函数
- NULL, //函数没有参数
- 0, //创建后直接运行
- NULL); //线程标识,不需要
- //创建线程2
- hThread2 = CreateThread(NULL, //默认安全级别
- 0, //默认栈大小
- Thread2Proc,//线程函数
- NULL, //函数没有参数
- 0, //创建后直接运行
- NULL); //线程标识,不需要
- //关闭线程句柄
- //当不再引用这个句柄时,立即将其关闭,减少其引用计数
- CloseHandle(hThread1);
- CloseHandle(hThread2);
- //主线程休眠4秒
- Sleep(4000);
- //关闭事件对象句柄
- DeleteCriticalSection(&g_csA);
- DeleteCriticalSection(&g_csB);
- return 0;
- }
- //线程1入口函数
- DWORD WINAPI Thread1Proc( LPVOID lpParameter)
- {
- while(TRUE)
- {
- EnterCriticalSection(&g_csA);
- Sleep(1);
- EnterCriticalSection(&g_csB);
- if(tickets > 0)
- {
- Sleep(1);
- printf("thread1 sell ticket : %d\n",tickets--);
- LeaveCriticalSection(&g_csB);
- LeaveCriticalSection(&g_csA);
- Sleep(1);
- }
- else
- {
- LeaveCriticalSection(&g_csB);
- LeaveCriticalSection(&g_csA);
- break;
- }
- }
- return 0;
- }
- //线程2入口函数
- DWORD WINAPI Thread2Proc( LPVOID lpParameter)
- {
- while(TRUE)
- {
- EnterCriticalSection(&g_csB);
- Sleep(1);
- EnterCriticalSection(&g_csA);
- if(tickets > 0)
- {
- Sleep(1);
- printf("thread2 sell ticket : %d\n",tickets--);
- LeaveCriticalSection(&g_csA);
- LeaveCriticalSection(&g_csB);
- Sleep(1);
- }
- else
- {
- LeaveCriticalSection(&g_csA);
- LeaveCriticalSection(&g_csB);
- break;
- }
- }
- return 0;
- }
在程序中,创建了两个临界区对象g_csA和g_csB。线程1中先尝试获取g_csA,获取成功后休眠,线程2尝试获取g_csB,成功后休眠,切换回线程1,然后线程1试图获取g_csB,因为g_csB已经被线程2获取,所以它线程1的获取不会成功,一直等待,直到自己的时间片结束后,转到线程2,线程2获取g_csB后,试图获取g_csA,当然也不会成功,转回线程1……这样交替等待,直到主线程睡醒,执行完毕,程序结束。
线程锁的概念函数EnterCriticalSection和LeaveCriticalSection的用法
注:使用结构CRITICAL_SECTION 需加入头文件#include “afxmt.h”
定义一个全局的锁 CRITICAL_SECTION的实例
和一个静态全局变量
- CRITICAL_SECTION cs;//可以理解为锁定一个资源
- static int n_AddValue = 0;//定义一个静态的全部变量n_AddValue
创建两个线程函数,代码实现如下:
- //第一个线程
- UINT FirstThread(LPVOID lParam)
- {
- EnterCriticalSection(&cs);//加锁 接下来的代码处理过程中不允许其他线程进行操作,除非遇到LeaveCriticalSection
- for(int i = 0; i<10; i++){
- n_AddValue ++;
- cout << "n_AddValue in FirstThread is "<<n_AddValue <<endl;
- }
- LeaveCriticalSection(&cs);//解锁 到EnterCriticalSection之间代码资源已经释放了,其他线程可以进行操作
- return 0;
- }
- //第二个线程
- UINT SecondThread(LPVOID lParam)
- {
- EnterCriticalSection(&cs);//加锁
- for(int i = 0; i<10; i++){
- n_AddValue ++;
- cout << "n_AddValue in SecondThread is "<<n_AddValue <<endl;
- }
- LeaveCriticalSection(&cs);//解锁
- return 0;
- }
在主函数添加以下代码
- int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
- {
- int nRetCode = 0;
- // 初始化 MFC 并在失败时显示错误
- if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
- {
- // TODO: 更改错误代码以符合您的需要
- _tprintf(_T("错误: MFC 初始化失败/n"));
- nRetCode = 1;
- }
- else
- {
- InitializeCriticalSection(&cs);//初始化结构CRITICAL_SECTION
- CWinThread *pFirstThread,*pSecondThread;//存储函数AfxBeginThread返回的CWinThread指针
- pFirstThread = AfxBeginThread(FirstThread,LPVOID(NULL));//启动第一个线程
- pSecondThread = AfxBeginThread(SecondThread,LPVOID(NULL));//启动第二个线程
- HANDLE hThreadHandle[2];//
- hThreadHandle[0] = pFirstThread->m_hThread;
- hThreadHandle[1] = pSecondThread->m_hThread;
- //等待线程返回
- WaitForMultipleObjects(2,hThreadHandle,TRUE,INFINITE);
- }
- return nRetCode;
- }
输出:
n_AddValue in FirstThread is 1
n_AddValue in FirstThread is 2
n_AddValue in FirstThread is 3
n_AddValue in FirstThread is 4
n_AddValue in FirstThread is 5
n_AddValue in FirstThread is 6
n_AddValue in FirstThread is 7
n_AddValue in FirstThread is 8
n_AddValue in FirstThread is 9
n_AddValue in FirstThread is 10
n_AddValue in SecondThread is 11
n_AddValue in SecondThread is 12
n_AddValue in SecondThread is 13
n_AddValue in SecondThread is 14
n_AddValue in SecondThread is 15
n_AddValue in SecondThread is 16
n_AddValue in SecondThread is 17
n_AddValue in SecondThread is 18
n_AddValue in SecondThread is 19
n_AddValue in SecondThread is 20
如果把两个线程函数中的EnterCriticalSection和LeaveCriticalSection位置移到for循环中去,线程的执行顺序将会改变
输出也就跟着改变,如:
- //第一个线程
- UINT FirstThread(LPVOID lParam)
- {
- for(int i = 0; i<10; i++){
- EnterCriticalSection(&cs);//加锁 锁移到for循环内部里
- n_AddValue ++;
- cout << "n_AddValue in FirstThread is "<<n_AddValue <<endl;
- LeaveCriticalSection(&cs);//解锁
- }
- return 0;
- }
- //第二个线程
- UINT SecondThread(LPVOID lParam)
- {
- for(int i = 0; i<10; i++){
- EnterCriticalSection(&cs);//加锁
- n_AddValue ++;
- cout << "n_AddValue in SecondThread is "<<n_AddValue <<endl;
- LeaveCriticalSection(&cs);//解锁
- }
- return 0;
- }
其他代码不变,输出的结果如下:
n_AddValue in FirstThread is 1
n_AddValue in SecondThread is 2
n_AddValue in FirstThread is 3
n_AddValue in SecondThread is 4
n_AddValue in FirstThread is 5
n_AddValue in SecondThread is 6
n_AddValue in FirstThread is 7
n_AddValue in SecondThread is 8
n_AddValue in FirstThread is 9
n_AddValue in SecondThread is 10
n_AddValue in FirstThread is 11
n_AddValue in SecondThread is 12
n_AddValue in FirstThread is 13
n_AddValue in SecondThread is 14
n_AddValue in FirstThread is 15
n_AddValue in SecondThread is 16
n_AddValue in FirstThread is 17
n_AddValue in SecondThread is 18
n_AddValue in FirstThread is 19
n_AddValue in SecondThread is 20
个人认为在函数EnterCriticalSection和LeaveCriticalSection中间的代码执行过程不会被其他线程干拢或者这么讲不允许其他线程中
的代码执行。这样可以有效防止一个全局变量在两个线程中同时被操作的可能性