不管是硬件临界资源,仍是软件临界资源,多个线程必须互斥地对它进行访问。每一个线程中访问临界资源的那段代码称为临界区(Critical Section)。
每一个线程中访问临界资源的那段程序称为临界区(Critical Section)(临界资源是一次仅容许一个线程使用的共享资源)。每次只准许一个线程进入临界区,进入后不容许其余线程进入。不管是硬件临界资源,仍是软件临界资源,多个线程必须互斥地对它进行访问。
windows的Critical Section提供了4个API,分别是:windows
void WINAPI InitializeCriticalSection( _Out_ LPCRITICAL_SECTION lpCriticalSection );
void WINAPI DeleteCriticalSection( _Inout_ LPCRITICAL_SECTION lpCriticalSection );
void WINAPI EnterCriticalSection( _Inout_ LPCRITICAL_SECTION lpCriticalSection );
void WINAPI LeaveCriticalSection( _Inout_ LPCRITICAL_SECTION lpCriticalSection );
临界区是一种轻量级机制,在某一时间内只容许一个线程执行某个给定代码段。一般在修改全局数据(如集合类)时会使用临界区。事件、多用户终端执行程序和信号量也用于多线程同步,但临界区与它们不一样,它并不老是执行向内核模式的控制转换,这一转换成本昂贵。稍后将会看到,要得到一个未占用临界区,事实上只须要对内存作出不多的修改,其速度很是快。只有在尝试得到已占用临界区时,它才会跳至内核模式。这一轻量级特性的缺点在于临界区只能用于对同一进程内的线程进行同步。多线程
经过InitializeCriticalSection完成初始化,经过DeleteCriticalSection结束控制;经过EnterCriticalSection开启控制,在这个阶段系统只执行下面的代码;其余进程等待他的结束,最后经过LeaveCriticalSection来结束这种控制。函数
咱们经过如下两段代码予以分析:线程
#include "stdafx.h" #include <Windows.h> #include <process.h> int num = 0; CRITICAL_SECTION cs; unsigned WINAPI ThreadFun1(void *arg) { int cnt = *(int*)arg; EnterCriticalSection(&cs); for (int i = 0; i < cnt; i++) { num += 1; printf("Inc "); Sleep(10); } LeaveCriticalSection(&cs); return 0; } unsigned WINAPI ThreadFun2(void *arg) { int cnt = *(int*)arg; EnterCriticalSection(&cs); for (int i = 0; i < cnt; i++) { num -= 1; printf("Dec "); Sleep(10); } LeaveCriticalSection(&cs); return 0; } int main() { int param = 50; HANDLE h[2]; InitializeCriticalSection(&cs); h[0]= (HANDLE)_beginthreadex(NULL, 0, ThreadFun1, ¶m, 0, NULL); if (h[0] == 0) { printf("Can not create a thread 1.\n"); return 0; } h[1] = (HANDLE)_beginthreadex(NULL, 0, ThreadFun2, ¶m, 0, NULL); if (h[1] == 0) { printf("Can not create a thread 2.\n"); return 0; } WaitForMultipleObjects(2, h, true, INFINITE); DeleteCriticalSection(&cs); printf("The num is %d, and end of main.\n", num); return 0; }
咱们在每一个进程的计算过程均使用了临界资源进行了控制,所以在这两个进程的执行过程当中,其计算循环都是独立完成的,其运行结果以下:code
Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Dec Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc Inc The num is 0, and end of main. 请按任意键继续. . .
从结果上咱们能够明细看出,两个函数的循环计算过程当中,没有其余进程参与进来是独立运行完成的;进程
若是咱们将控制只独立在计算自己而不是控制到循环全过程,代码以下:事件
#include "stdafx.h" #include <Windows.h> #include <process.h> int num = 0; CRITICAL_SECTION cs; unsigned WINAPI ThreadFun1(void *arg) { int cnt = *(int*)arg; for (int i = 0; i < cnt; i++) { EnterCriticalSection(&cs); num += 1; printf("Inc "); LeaveCriticalSection(&cs); Sleep(10); } return 0; } unsigned WINAPI ThreadFun2(void *arg) { int cnt = *(int*)arg; for (int i = 0; i < cnt; i++) { EnterCriticalSection(&cs); num -= 1; printf("Dec "); LeaveCriticalSection(&cs); Sleep(10); } return 0; } int main() { int param = 50; HANDLE h[2]; InitializeCriticalSection(&cs); h[0]= (HANDLE)_beginthreadex(NULL, 0, ThreadFun1, ¶m, 0, NULL); if (h[0] == 0) { printf("Can not create a thread 1.\n"); return 0; } h[1] = (HANDLE)_beginthreadex(NULL, 0, ThreadFun2, ¶m, 0, NULL); if (h[1] == 0) { printf("Can not create a thread 2.\n"); return 0; } WaitForMultipleObjects(2, h, true, INFINITE); DeleteCriticalSection(&cs); printf("The num is %d, and end of main.\n", num); return 0; }
将控制移到循环体内,这样只能保证计算自己是独立的,而循环是无控制的,其运行结果就变得混乱了:ip
Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec Inc Dec The num is 0, and end of main.