C++拾遺--多線程:臨界區解決子線程的互斥

                  C++拾遺--多線程:臨界區解決子線程的互斥

前言

    爲了解決子線程的互斥問題,windows系統提出了關鍵段或臨界區(CRITICAL_SECTION)的概念。它一共有四個共兩對操作:初始化、銷燬,進入、離開。它們定義在頭文件synchapi.h中。

1.初始化變量

VOID WINAPI InitializeCriticalSection(
    LPCRITICAL_SECTION lpCriticalSection
    );

2.銷燬變量

VOID WINAPI DeleteCriticalSection(
    LPCRITICAL_SECTION lpCriticalSection
    );

3.進入臨界區域

VOID WINAPI EnterCriticalSection(
    LPCRITICAL_SECTION lpCriticalSection
    );

函數說明:系統保證各個子線程互斥的進入臨界區域

4.離開臨界區域

VOID WINAPI LeaveCriticalSection(
    LPCRITICAL_SECTION lpCriticalSection
    );

四個函數的使用都相當簡單,傳入CRITICAL_SECTION類型的變量地址即可。


正文

程序示例

下面我們使用關鍵段來解決子線程的互斥問題,程序代碼類似於原子操作解決線程衝突。每一個子線程都對同一個全局變量累加10。這次我們開啓50個子線程,查看最後的累加結果。

#include <stdio.h>
#include <stdlib.h>
#include <process.h>
#include <Windows.h>
#define Thread_NUM 50
CRITICAL_SECTION cs;
int g_count = 0;
void count(void *p)
{
	Sleep(100);    //do some work  
	//每個線程把g_count加1共10次  
	for (int i = 0; i < 10; i++)
	{
		//進入臨界區域
		EnterCriticalSection(&cs);
		g_count++;
		//離開臨界區域
		LeaveCriticalSection(&cs);
	}
	Sleep(100);   //do some work  
}
int main(void)
{
	printf("******臨界區解決子線程衝突演示***by David***\n");
	//初始化關鍵段變量cs
	InitializeCriticalSection(&cs);
	//共創建Thread_NUM個線程  
	HANDLE handles[Thread_NUM];
	//共驗證10次
	for (int i = 0; i < 10; i++)
	{
		for (int j = 0; j < Thread_NUM; j++)
		{
			handles[j] = _beginthread(count, 0, NULL);
		}
		WaitForMultipleObjects(Thread_NUM, handles, 1, INFINITE);
		printf("%d time g_count = %d\n", i, g_count);
		//重置  
		g_count = 0;
	}
	//銷燬關鍵段變量cs
	DeleteCriticalSection(&cs);
	getchar();
	return 0;
}
運行



從運行結果看,使用關鍵段確實可以解決子線程的衝突問題。在g_count++;的前後我們加上關鍵段的進入和離開,使這句代碼成爲了“臨界”區域。至此,g_count++;的操作就十分類似於原子操作。下面我們來詳細分析下關鍵段的使用原理。


關鍵段原理

關鍵段的定義

在minwinbase.h中

typedef RTL_CRITICAL_SECTION CRITICAL_SECTION;

而在winnt.h中

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;


關鍵段就是一結構體。具體原因後續分析……





本專欄目錄

所有內容的目錄

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