C++系列 --- 線程同步臨界區原理

1、臨界區對象

臨界區對象是定義在數據段中的一個CRITICAL_SECTION結構,Windows內部使用這個結構記錄一些同步信息,確保在同一段時間只有一個線程訪問數據段中的數據。

臨界區對象相關函數:

// 初始化臨界區對象資源
void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);

// 進入臨界區
void EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);

// 離開臨界區
void LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);

// 刪除臨界區對象資源
void DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection);

函數參數lpCriticalSection爲指向臨界區源的對象

2、臨界區對象實例 

#include <iostream>
#include <Windows.h>
#include <process.h>
using namespace std;

int g_Cnt = 0; // 計數器
CRITICAL_SECTION g_cs;  // 臨界區對象
BOOL bFlag = FALSE;// 判斷是否創建輔助線程

UINT _stdcall ThreadFunc(LPVOID lpParam);


int main()
{
	UINT uId[2];

	HANDLE h[2];

	::InitializeCriticalSection(&g_cs);  // 初始化臨界區資源

	h[0] = (HANDLE)::_beginthreadex(NULL, 0, ThreadFunc, NULL, 0, &uId[0]);
	h[1] = (HANDLE)::_beginthreadex(NULL, 0, ThreadFunc, NULL, 0, &uId[1]);

	// 標誌創建了輔助線程
	bFlag = TRUE;  
	// 主線程休息1000ms,讓出CPU的使用權,讓我們的輔助線程有運行的機會
	Sleep(1000);

	// 主線程運行
	bFlag = FALSE;

	::WaitForMultipleObjects(2, h, TRUE, INFINITE);

	// 關閉句柄
	CloseHandle(h[0]);
	CloseHandle(h[1]);

	::DeleteCriticalSection(&g_cs);  // 清除臨界區資源

	printf("g_Cnt = %d\n", g_Cnt);

	system("pause");
	return 0;
}

UINT _stdcall ThreadFunc(LPVOID lpParam)
{
	if (bFlag)
	{
		::EnterCriticalSection(&g_cs); 
		for (int i =0;i< 10000;i++)
			g_Cnt++;
		::LeaveCriticalSection(&g_cs);
		
	}

	return 0;
}

臨界區對象能很好的保護共享數據,但是它不能夠用於進程之間資源的鎖定,因爲它不是內核對象,如果要在進程間維持線程的同步,可以使用事件內核對象。 

3、互鎖函數

互鎖函數爲同步訪問多線程共享變量提供了一個簡單的機制。

如果變量在共享內存,不同進程的線程也可以使用此機制。

互鎖函數:

// 原子自加操作
InterlockedIncrement


// 原子自減操作
InterlockedDecrement


InterlockedExchangeAdd


InterlockedExchangePointer

4、實例 

#include <iostream>
#include <Windows.h>
#include <process.h>
using namespace std;

int g_Cnt = 0; // 計數器
BOOL bFlag = FALSE;// 判斷是否創建輔助線程

UINT _stdcall ThreadFunc(LPVOID lpParam);

int main()
{
	UINT uId[2];
	HANDLE h[2];

	h[0] = (HANDLE)::_beginthreadex(NULL, 0, ThreadFunc, NULL, 0, &uId[0]);
	h[1] = (HANDLE)::_beginthreadex(NULL, 0, ThreadFunc, NULL, 0, &uId[1]);

	// 標誌創建了輔助線程
	bFlag = TRUE;  
	// 主線程休息1000ms,讓出CPU的使用權,讓我們的輔助線程有運行的機會
	Sleep(1000);

	// 主線程運行
	bFlag = FALSE;

	::WaitForMultipleObjects(2, h, TRUE, INFINITE);

	// 關閉句柄
	CloseHandle(h[0]);
	CloseHandle(h[1]);

	printf("g_Cnt = %d\n", g_Cnt);

	system("pause");
	return 0;
}

UINT _stdcall ThreadFunc(LPVOID lpParam)
{
	if (bFlag)
	{
		for (int i = 0; i < 10000; i++)
			// 原子自加操作,不會被中斷
			InterlockedIncrement((long *)&g_Cnt);
	}

	return 0;
}

通過本節的學習,我們知道了如何控制關鍵的代碼段不被系統打斷,可以通過臨界區或者互鎖函數來實現關鍵代碼段不被系統打斷,可以保證數據得出正確的結果!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章