1. 程式人生 > >秒殺多執行緒第七篇 經典執行緒同步 互斥量Mutex

秒殺多執行緒第七篇 經典執行緒同步 互斥量Mutex

閱讀本篇之前推薦閱讀以下姊妹篇:

秒殺多執行緒第四篇一個經典的多執行緒同步問題

秒殺多執行緒第五篇經典執行緒同步關鍵段CS

秒殺多執行緒第六篇經典執行緒同步事件Event

 

前面介紹了關鍵段CS事件Event經典執行緒同步問題中的使用。本篇介紹用互斥量Mutex來解決這個問題。

互斥量也是一個核心物件,它用來確保一個執行緒獨佔一個資源的訪問。互斥量與關鍵段的行為非常相似,並且互斥量可以用於不同程序中的執行緒互斥訪問資源。使用互斥量Mutex主要將用到四個函式。下面是這些函式的原型和使用說明。

第一個 CreateMutex

函式功能:建立互斥量(注意與事件Event

的建立函式對比)

函式原型:

HANDLECreateMutex(

  LPSECURITY_ATTRIBUTESlpMutexAttributes,

  BOOLbInitialOwner,     

  LPCTSTRlpName

);

函式說明:

第一個引數表示安全控制,一般直接傳入NULL

第二個引數用來確定互斥量的初始擁有者。如果傳入TRUE表示互斥量物件內部會記錄建立它的執行緒的執行緒ID號並將遞迴計數設定為1,由於該執行緒ID非零,所以互斥量處於未觸發狀態。如果傳入

FALSE,那麼互斥量物件內部的執行緒ID號將設定為NULL,遞迴計數設定為0,這意味互斥量不為任何執行緒佔用,處於觸發狀態。

第三個引數用來設定互斥量的名稱,在多個程序中的執行緒就是通過名稱來確保它們訪問的是同一個互斥量。

函式訪問值:

成功返回一個表示互斥量的控制代碼,失敗返回NULL

 

第二個開啟互斥量

函式原型:

HANDLEOpenMutex(

 DWORDdwDesiredAccess,

 BOOLbInheritHandle,

 LPCTSTRlpName    

//名稱

);

函式說明:

第一個引數表示訪問許可權,對互斥量一般傳入MUTEX_ALL_ACCESS。詳細解釋可以檢視MSDN文件。

第二個引數表示互斥量控制代碼繼承性,一般傳入TRUE即可。

第三個引數表示名稱。某一個程序中的執行緒建立互斥量後,其它程序中的執行緒就可以通過這個函式來找到這個互斥量。

函式訪問值:

成功返回一個表示互斥量的控制代碼,失敗返回NULL

 

第三個觸發互斥量

函式原型:

BOOLReleaseMutex (HANDLEhMutex)

函式說明:

訪問互斥資源前應該要呼叫等待函式,結束訪問時就要呼叫ReleaseMutex()來表示自己已經結束訪問,其它執行緒可以開始訪問了。

 

最後一個清理互斥量

由於互斥量是核心物件,因此使用CloseHandle()就可以(這一點所有核心物件都一樣)。

 

接下來我們就在經典多執行緒問題用互斥量來保證主執行緒與子執行緒之間的同步,由於互斥量的使用函式類似於事件Event,所以可以仿照上一篇的實現來寫出程式碼

//經典執行緒同步問題 互斥量Mutex
#include <stdio.h>
#include <process.h>
#include <windows.h>

long g_nNum;
unsigned int __stdcall Fun(void *pPM);
const int THREAD_NUM = 10;
//互斥量與關鍵段
HANDLE  g_hThreadParameter;
CRITICAL_SECTION g_csThreadCode;

int main()
{
	printf("     經典執行緒同步 互斥量Mutex\n");
	printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");
	
	//初始化互斥量與關鍵段 第二個引數為TRUE表示互斥量為建立執行緒所有
	g_hThreadParameter = CreateMutex(NULL, FALSE, NULL);
	InitializeCriticalSection(&g_csThreadCode);

	HANDLE  handle[THREAD_NUM];	
	g_nNum = 0;	
	int i = 0;
	while (i < THREAD_NUM) 
	{
		handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);
		WaitForSingleObject(g_hThreadParameter, INFINITE); //等待互斥量被觸發
		i++;
	}
	WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
	
	//銷燬互斥量和關鍵段
	CloseHandle(g_hThreadParameter);
	DeleteCriticalSection(&g_csThreadCode);
	for (i = 0; i < THREAD_NUM; i++)
		CloseHandle(handle[i]);
	return 0;
}
unsigned int __stdcall Fun(void *pPM)
{
	int nThreadNum = *(int *)pPM;
	ReleaseMutex(g_hThreadParameter);//觸發互斥量
	
	Sleep(50);//some work should to do

	EnterCriticalSection(&g_csThreadCode);
	g_nNum++;
	Sleep(0);//some work should to do
	printf("執行緒編號為%d  全域性資源值為%d\n", nThreadNum, g_nNum);
	LeaveCriticalSection(&g_csThreadCode);
	return 0;
}

執行結果如下圖:

可以看出,與關鍵段類似,互斥量也是不能解決執行緒間的同步問題。

       聯想到關鍵段會記錄執行緒ID即有“執行緒擁有權”的,而互斥量也記錄執行緒ID,莫非它也有“執行緒擁有權”這一說法。

       答案確實如此,互斥量也是有“執行緒擁有權”概念的。“執行緒擁有權”在關鍵段中有詳細的說明,這裡就不再贅述了。另外由於互斥量常用於多程序之間的執行緒互斥,所以它比關鍵段還多一個很有用的特性——“遺棄”情況的處理。比如有一個佔用互斥量的執行緒在呼叫ReleaseMutex()觸發互斥量前就意外終止了(相當於該互斥量被“遺棄”了),那麼所有等待這個互斥量的執行緒是否會由於該互斥量無法被觸發而陷入一個無窮的等待過程中了?這顯然不合理。因為佔用某個互斥量的執行緒既然終止了那足以證明它不再使用被該互斥量保護的資源,所以這些資源完全並且應當被其它執行緒來使用。因此在這種“遺棄”情況下,系統自動把該互斥量內部的執行緒ID設定為0,並將它的遞迴計數器復置為0,表示這個互斥量被觸發了。然後系統將公平地選定一個等待執行緒來完成排程(被選中的執行緒的WaitForSingleObject()會返回WAIT_ABANDONED_0)。

 

下面寫二個程式來驗證下:

第一個程式建立互斥量並等待使用者輸入後就觸發互斥量。第二個程式先開啟互斥量,成功後就等待並根據等待結果作相應的輸出。詳見程式碼:

第一個程式:

#include <stdio.h>
#include <conio.h>
#include <windows.h>
const char MUTEX_NAME[] = "Mutex_MoreWindows";
int main()
{
	HANDLE hMutex = CreateMutex(NULL, TRUE, MUTEX_NAME); //建立互斥量
	printf("互斥量已經建立,現在按任意鍵觸發互斥量\n");
	getch();
	//exit(0);
	ReleaseMutex(hMutex);
	printf("互斥量已經觸發\n");
	CloseHandle(hMutex);
	return 0;
}

第二個程式:

#include <stdio.h>
#include <windows.h>
const char MUTEX_NAME[] = "Mutex_MoreWindows";
int main()
{
	HANDLE hMutex = OpenMutex(MUTEX_ALL_ACCESS, TRUE, MUTEX_NAME); //開啟互斥量
	if (hMutex == NULL)
	{
		printf("開啟互斥量失敗\n");
		return 0;
	}
	printf("等待中....\n");
	DWORD dwResult = WaitForSingleObject(hMutex, 20 * 1000); //等待互斥量被觸發
	switch (dwResult)
	{
	case WAIT_ABANDONED:
		printf("擁有互斥量的程序意外終止\n");
		break;

	case WAIT_OBJECT_0:
		printf("已經收到訊號\n");
		break;

	case WAIT_TIMEOUT:
		printf("訊號未在規定的時間內送到\n");
		break;
	}
	CloseHandle(hMutex);
	return 0;
}

運用這二個程式時要先啟動程式一再啟動程式二。下面展示部分輸出結果:

結果一.二個程序順利執行完畢:

結果二.將程式一中//exit(0);前面的註釋符號去掉,這樣程式一在觸發互斥量之前就會因為執行exit(0);語句而且退出,程式二會收到WAIT_ABANDONED訊息並輸出“擁有互斥量的程序意外終止”:

有這個對“遺棄”問題的處理,在多程序中的執行緒同步也可以放心的使用互斥量。

 

最後總結下互斥量Mutex

1.互斥量是核心物件,它與關鍵段都有“執行緒所有權”所以不能用於執行緒的同步。

2.互斥量能夠用於多個程序之間執行緒互斥問題,並且能完美的解決某程序意外終止所造成的“遺棄”問題。

 

下一篇《秒殺多執行緒第八篇 經典執行緒同步 訊號量Semaphore》將介紹使用訊號量Semaphore來解決這個經典執行緒同步問題。

 

 

轉載請標明出處,原文地址:http://blog.csdn.net/morewindows/article/details/7470936

如果覺得本文對您有幫助,請點選支援一下,您的支援是我寫作最大的動力,謝謝。


 

再分享一下我老師大神的人工智慧教程吧。零基礎!通俗易懂!風趣幽默!希望你也加入到我們人工智慧的隊伍中來!http://www.captainbed.net