一、問題回顧
我們上一篇文章最后的程序的輸出 g_Count 的值不是每次都正確,原因是沒有對全局資源 g_Count
進行互斥訪問(就是同一時刻只能由一個線程訪問),接下來我們就來說一下使用關鍵段來給全局資源加鎖以實現互斥訪問。
這是上一篇中的程序:
#include <stdio.h>
#include <windows.h>
const unsigned int THREAD_NUM = 50;
unsigned int g_Count = 0;
DWORD WINAPI ThreadFunc(LPVOID);
int main()
{
HANDLE hThread[THREAD_NUM];
for (int i = 0; i < THREAD_NUM; i++)
{
hThread[i] = CreateThread(NULL, 0, ThreadFunc, 0, 0, NULL); // 創建線程
}
WaitForMultipleObjects(THREAD_NUM, hThread, true, INFINITE); //一直等待,直到所有子線程全部返回
printf(" 總共 %d 個線程給 g_Count 的值加一,現在 g_Count = %d\n", THREAD_NUM, g_Count);
return 0;
}
DWORD WINAPI ThreadFunc(LPVOID p)
{
Sleep(50);
g_Count++;
Sleep(50);
return 0;
}
二、 關鍵段 CriticalSection 聲明及相關函數
(一)CriticalSection 聲明
CRITICAL_SECTION 關鍵段名字; // eg: CRITICAL_SECTION cs;
CRITICAL_SECTION 結構說明:在 vs 中 先聲明一個 關鍵段, 鼠標放到 CRITICAL_SECTION
關鍵字上按 F12 轉到定義如下:
typedef RTL_CRITICAL_SECTION CRITICAL_SECTION;
再把鼠標放到 RTL_CRITICAL_SECTION 上按 F12 即可轉到 CRITICAL_SECTION 結構體的定義 如下:
typedef struct _RTL_CRITICAL_SECTION {
PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
//
// The following three fields control entering and exiting the critical
// section for the resource
//
LONG LockCount;
LONG RecursionCount;
HANDLE OwningThread; // from the thread's ClientId->UniqueThread
HANDLE LockSemaphore;
ULONG_PTR SpinCount; // force size on 64-bit systems when packed
} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;
第一個參數:PRTL_CRITICAL_SECTION_DEBUG DebugInfo; 調試的時候用的,先不做介紹。
第二個參數:LONG LockCount; 初始化為-1,n表示有n個線程在等待。
第三個參數:LONG RecursionCount; 表示該關鍵段的擁有線程對此資源獲得關鍵段次數,初為0。
第四個參數:HANDLE OwningThread; 即擁有該關鍵段的線程句柄
第五個參數:HANDLE LockSemaphore; 實際上是一個自復位事件。
第六個參數:ULONG_PTR SpinCount; 旋轉鎖的設置,用於多處理器。
(二)CriticalSection相關函數
1.函數功能:初始化,定義關鍵段變量后必須先初始化。
void InitializeCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);
2.函數功能:銷毀,用完之后記得銷毀。
void DeleteCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);
3.函數功能:進入關鍵區域,系統保證各線程互斥的進入關鍵區域。
void EnterCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);
4.函數功能:離開關關鍵區域
void LeaveCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);
三、實例
現在使用關鍵段來解決上面的問題,代碼如下:
#include <stdio.h>
#include <windows.h>
const unsigned int THREAD_NUM = 50;
unsigned int g_Count = 0;
CRITICAL_SECTION cs; //聲明關鍵段
DWORD WINAPI ThreadFunc(LPVOID);
int main()
{
InitializeCriticalSection(&cs); // 初始化關鍵段
HANDLE hThread[THREAD_NUM];
for (int i = 0; i < THREAD_NUM; i++)
{
hThread[i] = CreateThread(NULL, 0, ThreadFunc, 0, 0, NULL); // 創建線程
}
WaitForMultipleObjects(THREAD_NUM, hThread, true, INFINITE); //一直等待,直到所有子線程全部返回
printf(" 總共 %d 個線程給 g_Count 的值加一,現在 g_Count = %d\n", THREAD_NUM, g_Count);
DeleteCriticalSection(&cs); //銷毀關鍵段
return 0;
}
DWORD WINAPI ThreadFunc(LPVOID p)
{
Sleep(50);
EnterCriticalSection(&cs); // 進入關鍵段
g_Count++;
LeaveCriticalSection(&cs); // 離開關鍵段
Sleep(50);
return 0;
}
運行結果如下圖所示,給全局資源 g_Count 加鎖,實現互斥訪問,就能夠讓每個線程正確給 g_Count 值加一 :