天天看點

C++ 經典線程同步互斥量Mutex 示例解析(十二)

在windows系統中,系統本身為我們提供了很多鎖。通過這些鎖的使用,一方面可以加強我們對鎖的認識,另外一方面可以提高代碼的性能和健壯性。常用的鎖以下四種:

臨界區:C++ 關鍵段(Critical Section)CS深入淺出 之多線程(七)

event :C++ 經典線程同步 事件Event(九)

信号量:信号量是使用的最多的一種鎖結果,也是最友善的一種鎖。圍繞着信号量,人們提出了很多資料互斥通路的方案,pv操作就是其中的一種。如果說互斥鎖隻能對單個資源進行保護,那麼信号量可以對多個資源進行保護。同時信号量在解鎖的時候,可以被另外一個thread進行解鎖操作。

互斥量:将是這篇文章所接觸到的!

ok!本次Demo參考于MSDN:MSDN

1 首先來熟悉我們将要接觸到的系列函數

CreateMutex:

HANDLE WINAPI CreateMutex(
  _In_opt_  LPSECURITY_ATTRIBUTES lpMutexAttributes,
  _In_      BOOL bInitialOwner,
  _In_opt_  LPCTSTR lpName
);
           

函數參數說明:(CreateMutex:)

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

第二個參數用來确定互斥量的初始擁有者。如果傳入TRUE表示互斥量對象内部會記錄建立它的線程的線程ID号并将遞歸計數設定為1,由于該線程ID非零,是以互斥量處于未觸發狀态。如果傳入FALSE,那麼互斥量對象内部的線程ID号将設定為NULL,遞歸計數設定為0,這意味互斥量不為任何線程占用,處于觸發狀态。

第三個參數用來設定互斥量的名稱,在多個程序中的線程就是通過名稱來確定它們通路的是同一個互斥量。

函數通路值:

成功傳回一個表示互斥量的句柄,失敗傳回NULL。

備注:通過CreateMutex傳回的句柄有MUTEX_ALL_ACCESS通路權,它可以在需要的句柄互斥對象的任何函數中使用,前提是調用者已被授予通路權限。如果一個互斥體是由一個服務或一個線程正在模拟不同的使用者建立的,你可以申請一個安全描述符來互斥,當你建立它,或者通過改變其預設的DACL更改為建立程序的預設安全描述符

OpenMutex:

HANDLE OpenMutex(
DWORDdwDesiredAccess, // access
BOOLbInheritHandle, // inheritance option
LPCTSTRlpName // object name
);
           

參數說明:

第一個參數表示通路權限,對互斥量一般傳入MUTEX_ALL_ACCESS。詳細解釋可以檢視MSDN文檔。

第二個參數表示互斥量句柄繼承性,一般傳入TRUE即可。

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

函數通路值:成功傳回一個表示互斥量的句柄,失敗傳回NULL。

ReleaseMutex:

BOOL WINAPI ReleaseMutex(
  _In_  HANDLE hMutex
);
           

參數說明:通路互斥資源前應該要調用等待函數,結束通路時就要調用ReleaseMutex()來表示自己已經結束通路,其它線程可以開始通路了。

給出CSDN的demo,demo中也有比較詳細的注釋!

#include <windows.h>
#include <stdio.h>

#define THREADCOUNT 4 

HANDLE ghWriteEvent; 
HANDLE ghThreads[THREADCOUNT];

DWORD WINAPI ThreadProc(LPVOID);

void CreateEventsAndThreads(void) 
{
	int i; 
	DWORD dwThreadID; 

	// Create a manual-reset event object. The write thread sets this
	// object to the signaled state when it finishes writing to a 
	// shared buffer. 

	ghWriteEvent = CreateEvent( 
		NULL,               // default security attributes
		TRUE,               // manual-reset event
		FALSE,              // initial state is nonsignaled
		TEXT("WriteEvent")  // object name
		); 

	if (ghWriteEvent == NULL) 
	{ 
		printf("CreateEvent failed (%d)\n", GetLastError());
		return;
	}

	// Create multiple threads to read from the buffer.

	for(i = 0; i < THREADCOUNT; i++) 
	{
		// TODO: More complex scenarios may require use of a parameter
		//   to the thread procedure, such as an event per thread to  
		//   be used for synchronization.
		ghThreads[i] = CreateThread(
			NULL,              // default security
			0,                 // default stack size
			ThreadProc,        // name of the thread function
			NULL,              // no thread parameters
			0,                 // default startup flags
			&dwThreadID); 

		if (ghThreads[i] == NULL) 
		{
			printf("CreateThread failed (%d)\n", GetLastError());
			return;
		}
	}
}

void WriteToBuffer(VOID) 
{
	// TODO: Write to the shared buffer.

	printf("Main thread writing to the shared buffer...\n");

	// Set ghWriteEvent to signaled

	if (! SetEvent(ghWriteEvent) ) 
	{
		printf("SetEvent failed (%d)\n", GetLastError());
		return;
	}
}

void CloseEvents()
{
	// Close all event handles (currently, only one global handle).

	CloseHandle(ghWriteEvent);
}

int main( void )
{
	DWORD dwWaitResult;

	// TODO: Create the shared buffer

	// Create events and THREADCOUNT threads to read from the buffer

	CreateEventsAndThreads();

	// At this point, the reader threads have started and are most
	// likely waiting for the global event to be signaled. However, 
	// it is safe to write to the buffer because the event is a 
	// manual-reset event.

	WriteToBuffer();

	printf("Main thread waiting for threads to exit...\n");

	// The handle for each thread is signaled when the thread is
	// terminated.
	dwWaitResult = WaitForMultipleObjects(
		THREADCOUNT,   // number of handles in array
		ghThreads,     // array of thread handles
		TRUE,          // wait until all are signaled
		INFINITE);

	switch (dwWaitResult) 
	{
		// All thread objects were signaled
	case WAIT_OBJECT_0: 
		printf("All threads ended, cleaning up for application exit...\n");
		break;

		// An error occurred
	default: 
		printf("WaitForMultipleObjects failed (%d)\n", GetLastError());
		return 1;
	} 

	// Close the events to clean up

	CloseEvents();

	system("pause");
	return 0;
}

DWORD WINAPI ThreadProc(LPVOID lpParam) 
{
	// lpParam not used in this example.
	UNREFERENCED_PARAMETER(lpParam);

	DWORD dwWaitResult;

	printf("Thread %d waiting for write event...\n", GetCurrentThreadId());

	dwWaitResult = WaitForSingleObject( 
		ghWriteEvent, // event handle
		INFINITE);    // indefinite wait

	switch (dwWaitResult) 
	{
		// Event object was signaled
	case WAIT_OBJECT_0: 
		//
		// TODO: Read from the shared buffer
		//
		printf("Thread %d reading from buffer\n", 
			GetCurrentThreadId());
		break; 

		// An error occurred
	default: 
		printf("Wait error (%d)\n", GetLastError()); 
		return 0; 
	}

	// Now that we are done reading the buffer, we could use another
	// event to signal that this thread is no longer reading. This
	// example simply uses the thread handle for synchronization (the
	// handle is signaled when the thread terminates.)

	printf("Thread %d exiting\n", GetCurrentThreadId());
	return 1;
}
           

運作效果如下:

C++ 經典線程同步互斥量Mutex 示例解析(十二)

最後總結下互斥量Mutex:

1.互斥量是核心對象,它與關鍵段都有“線程所有權”是以不能用于線程的同步。

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

繼續閱讀