MFC线程(二):线程同步临界区CRITICAL SECTION
当多个线程同时使用相同的资源时,由于是并发执行,不能保证先后顺序.所以假如时一个公共变量被几个线程同时使用会造成该变量值的混乱.
下面来举个简单例子.
假如有一个字符数组变量
char g_charArray[4];
CString szResult;
AfxBeginThread(FunOne,NULL); //FunOne给数组赋值全为S
AfxBeginThread(FunTwo,NULL); //FunTwo也给数组赋值全为B
AfxBeginThread(GetResult,NULL); //得到数组的值,本来期望的值为最后一次赋的值BBBB.但实际结果是不确定的,可能是SBSB,SSBB或者其它
UINT FunOne(LPVOID pParam){ //给数组赋值
for(int i =0; i < 4; i++){
g_charArray[i] = 'S';
Sleep(10);
}
return 0;
}
UINT FunTwo(LPVOID pParam){//得到数组值
for(int i =0; i < 4; i++){
g_charArray[i] = 'B';
Sleep(10);
}
return 0;
}
UINT GetResult(LPVOID pParam){//给数组赋值
szResult = CString(g_charArray);
return 0;
}
Win32 API中临界区(Critical Section)
为了使公共变量不会被毫无秩序,混乱的更改.我们希望一个线程使用这个变量时其他线程就不能使用,只能等别的线程用完了才用.
于是出现了临界区这说法,相当于把使用到的变量的内存看作一块区域,当某个线程使用时就进入该区域,使用完了离开.当有一个线程进入该区域时其他线程就只能在外面等.临界区的使用如下.
临界区只能用于同一个进程内的线程同步.如果想要多个进程间的线程同步就不能用它了.
CRITICAL_SECTION g_criSection //定义临界区变量
void MainTestFun{
InitializeCriticalSection(&g_criSection); //使用前必须初始化
char g_charArray[4];
CString szResult;
AfxBeginThread(FunOne,NULL);
AfxBeginThread(FunTwo,NULL);
AfxBeginThread(GetResult,); //此时得到的值总是BBBB或者SSSS,哪一个线程拥有临界区不确定.只能保证某一个时间只能有一个线程拥有
//DeleteCriticalSection(&g_criSection); 使用完了临界区就要删除掉,但是在这里这样使用可能会出错.因为必须保证在删除时这个临界区没有再被使用了.所以如果一个类中,一般把它放析构函数中去调用.
}
//在线程调用的函数的开始调用EnterCriticalSection结束时调用CriticalSection(&g_criSection);
//在中间有用到的所有资源在被使用时其他线程不能使用(不能读也不能改写).其它两个函数FunOne,GetResult按同样的方法添加这两行代码.这里不再写出来了.
UINT FunOne(LPVOID pParam){
EnterCriticalSection(&g_criSection); //表示进入临界区
for(int i =0; i < 4; i++){
g_charArray[i] = 'S';
Sleep(1);
}
LeaveCriticalSection(&g_criSection); //离开临界区
return 0;
}
MFC中的临界区类
MFC把上面的操作封装成一个类CCriticalSection,使用函数Lock与Unlock表示进入和离开临界区.上锁和解锁的说法是更符合我们的习惯思维的.
#include "afxmt.h" //需要添加该头文件引用
CCriticalSection g_criSection;
void MainTestFun{
char g_charArray[4];
CString szResult;
AfxBeginThread(FunOne,NULL);
AfxBeginThread(FunTwo,NULL);
AfxBeginThread(GetResult,); //此时得到的值总是BBBB或者SSSS,哪一个线程拥有临界区不确定.只能保证某一个时间只能有一个线程拥有 }
//其它两个类也同样加上这两行代码
UINT FunOne(LPVOID pParam){
g_criSection.Lock()//给所有使用的资源上锁
for(int i =0; i < 4; i++){
g_charArray[i] = 'S';
Sleep(1);
}
g_criSection.UnLock(); //给使用的资源解锁
return 0;
}
其他线程同步的方法
使线程同步共有4种方法:
1.临界区(critical section)
2.事件(event)
3.信号量(Semaphore)
4.互拆量(Mutex)
其中1临界区是用户对象,所以没太多权限,只能处理同一个进程内的线程同步
而剩下的2,3,4都是内核对象,权限比较大,可以跨进程使用,因此能够处理不同进程间的线程同步问题.
其中的Mutext和临界区适用的场景基本上一样,只不过Mutext是内核对象,而critical section是用户对象