首先了解一下线程互斥的概念,线程互斥说白了就是在进程中多个线程的相互制约,如线程A未执行完毕,其他线程就需要等待!
线程之间的制约关系分为间接相互制约和直接相互制约。
所谓间接相互制约:一个系统中的多个线程必然要共享某种系统资源如共享CPU,共享打印机。间接制约即源于资源共享,线程A在打印的时候其他线程就要等待,否则打印的数据将变得非常混乱。间接相互制约称为互斥,互斥是同步的一种特殊形式
直接相互制约:主要指的是线程之间的一种递进关系,例如线程B运行的条件之一是需要线程A提供的参数,那么在线程A将数据传到线程B之前,线程B都将处于阻塞状态,称为同步。以后再说
(1)临界区,有的称为关键段,是定义在数据段中的一个CRITICAL_SECTION结构,确保在同一时间只有一个线程访问该数据段中的数据。计算机中大多数物理设备,进程中的共享变量等都是临界资源,它们要求被互斥访问,每个进程中访问的临界资源的代码称为临界区
写代码的时候可通过
CRITICAL_SECTION g_csThreadCode;
对临界区进行定义,但是在使用临界区之前首先要对临界区对象进行初始化,其函数原型如下:
InitializeCriticalSection(
_Out_ LPCRITICAL_SECTION lpCriticalSection
);
对临界区对象初始化完成后,线程访问临界区数据必须首先调用EnterCriticalSection函数申请进入临界区。在同一时间内,Windows只允许一个线程进入临界区。所以在申请的时候,如果有另一个线程在临界区的话,EnterCriticalSection函数将会一直等待下去,知道其他线程离开临界区才返回。EnterCriticalSection函数定义如下:
EnterCriticalSection(
_Inout_ LPCRITICAL_SECTION lpCriticalSection
);
当临界区对象操作完成后使用函数LeaveCriticalSection函数离开临界区,将临界区交还给Windows方便其他线程继续申请使用
LeaveCriticalSection(
_Inout_ LPCRITICAL_SECTION lpCriticalSection
);
用完临界区对象,使用DeleteCriticalSection函数将对象删除
DeleteCriticalSection(
_Inout_ LPCRITICAL_SECTION lpCriticalSection
);
例:用线程同时访问全局变量并对全局变量进行操作,如果不使用临界区访问,代码如下
#include <stdio.h>
#include <Windows.h>
#include <process.h>
int g_nCount1 = 0;
int g_nCount2 = 0;
BOOL g_bContinue = TRUE;
UINT _stdcall ThreadFun(LPVOID);
int main(int argc, char *argv[])
{
HANDLE threads[2];
threads[0] = (HANDLE)_beginthreadex(NULL, 0, ThreadFun, NULL, 0, NULL);
threads[1] = (HANDLE)_beginthreadex(NULL, 0, ThreadFun, NULL, 0, NULL);
//等待1秒,结束两个计数线程,关闭句柄
Sleep(1000);
g_bContinue = FALSE;
WaitForMultipleObjects(2, threads, TRUE, INFINITE);
CloseHandle(threads[0]);
CloseHandle(threads[1]);
printf("g_nCount1 = %d\n", g_nCount1);
printf("g_nCount2 = %d\n", g_nCount2);
return 0;
}
UINT _stdcall ThreadFun(LPVOID)
{
while (g_bContinue)
{
g_nCount1++;
g_nCount2++;
}
return 0;
}
运行结果截图如下: