關鍵區域(CriticalSection)
臨界區是為了確保同一個代碼片段在同一時間只能被一個線程訪問,與原子鎖不同的是臨界區是多條指令的鎖定,而原子鎖僅僅對單條操作指令有效;臨界區和原子鎖只能控制同一個進程中線程的同步
使用方法:
1、初始化:InitializeCriticalSection; 2、刪除:DeleteCriticalSection; 3、進入:EnterCriticalSection(可能造成阻塞); 4、嘗試進入:TryEnterCriticalSection(不會造成阻塞); 5、離開:LeaveCriticalSection;
固有特點(優點+缺點):
1、是一個用戶模式的對象,不是系統核心對象;
2、因為不是核心對象,所以執行速度快,有效率;
3、因為不是核心對象,所以不能跨進程使用;
4、可以多次“進入”,但必須多次“退出”;
5、最好不要同時進入或等待多個 Critical Sections,容易造成死鎖;
6、無法檢測到進入到 Critical Sections 里面的線程當前是否已經退出!
一般錯誤的情況:
#include <stdio.h> #include <windows.h> long g_nNum = 0 ; DWORD WINAPI ThreadProc(__in LPVOID lpParameter); const int THREAD_NUM = 10; int main() { HANDLE handle[THREAD_NUM]; g_nNum = 0; int var = 0; while ( var< THREAD_NUM) { handle[ var++] = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL); } WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE); for( var=0; var<sizeof(handle); var++) { CloseHandle(handle[var]); } return 0; } DWORD WINAPI ThreadProc(__in LPVOID lpParameter) { Sleep(50); g_nNum++; Sleep(0); printf("當前計數為:%d\n",g_nNum); return 0; }
運行2次結果:
用了關鍵區域的情況:
#include <stdio.h> #include <windows.h> long g_nNum = 0 ; DWORD WINAPI ThreadProc(__in LPVOID lpParameter); const int THREAD_NUM = 10; CRITICAL_SECTION g_ThreadCode; int main() { HANDLE handle[THREAD_NUM]; g_nNum = 0; int var = 0; InitializeCriticalSection(&g_ThreadCode); while ( var< THREAD_NUM) { handle[ var++] = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL); } WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE); DeleteCriticalSection( &g_ThreadCode ); for( var=0; var<sizeof(handle); var++) { CloseHandle(handle[var]); } return 0; } DWORD WINAPI ThreadProc(__in LPVOID lpParameter) { EnterCriticalSection( &g_ThreadCode ); g_nNum++; printf("當前計數為:%d\n",g_nNum); LeaveCriticalSection( &g_ThreadCode ); return 0; }