A classic multi-thread synchronization problem 04 Mutex Mutex

// Create a mutex (note the comparison with the event creation function)

HANDLECreateMutex(

  LPSECURITY_ATTRIBUTESlpMutexAttributes,

  BOOLbInitialOwner,     

  LPCTSTRlpName

);

Function description:

The first parameter represents the security control, usually NULL is directly passed in.

The second parameter is used to determine the initial owner of the mutex. If TRUE is passed in, it means that the thread ID number of the thread that created it will be recorded inside the mutex object and the recursion count is set to 1. Since the thread ID is non-zero, the mutex is not triggered. If FALSE is passed in, the thread ID number inside the mutex object will be set to NULL , and the recursion count will be set to 0 , which means that the mutex is not occupied by any thread and is in a triggered state.

The third parameter is used to set the name of the mutex. Threads in multiple processes use the name to ensure that they access the same mutex.

Function access value:

Returns a handle representing the mutex on success, NULL on failure .

// open mutex

HANDLEOpenMutex(

 DWORDdwDesiredAccess,

 BOOLbInheritHandle,

 LPCTSTRlpName //Name

);

Function description:

The first parameter indicates the access authority, and MUTEX_ALL_ACCESS is generally passed to the mutex . A detailed explanation can be found in the MSDN documentation.

The second parameter represents the inheritance of the mutex handle, and generally TRUE can be passed in.

The third parameter represents the name. After a thread in a process creates a mutex, threads in other processes can use this function to find the mutex.

Function access value:

Returns a handle representing the mutex on success, NULL on failure .

// trigger mutex

BOOLReleaseMutex (HANDLEhMutex)

// clean up mutex

CloseHandle()

Next, we use the mutex to ensure the synchronization between the main thread and the sub-thread in the classic multi-threading problem. Since the function of the mutex is similar to the event Event , we can write the code according to the implementation of the previous article :

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

long lGlobVar = 0;//Global resources
// Mutex and key segment
HANDLE hMutexPtrParam;
CRITICAL_SECTION csGlobVar;

unsigned int WINAPI ThreadProc(void* lpParam){
	//Because creating a thread requires a certain amount of overhead, the new thread cannot be executed here for the first time  
	int iThreadNo = * (int *) lpParam;
	ReleaseMutex(hMutexPtrParam);//Trigger mutex   
	Sleep(50);//Do Sth

	EnterCriticalSection(&csGlobVar);
	lGlobVar++;//Process global resources
	Sleep(50);//Do Sth
	printf("Thread number: %d, global variable: %d.\n", iThreadNo, lGlobVar);
	LeaveCriticalSection(&csGlobVar);
	return 0;
}

void main()
{
	//Initialize the mutex and the key segment The second parameter is TRUE, indicating that the mutex is owned by the creation thread  
	hMutexPtrParam = CreateMutex(NULL, FALSE, NULL);
	InitializeCriticalSection(&csGlobVar);


	HANDLE hThreads[10];
	for(int i = 0; i < 10; i++){//The main thread may change the value of i when the sub-thread receives the parameter  
		hThreads[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadProc, &i, 0, NULL);//Create a thread
		WaitForSingleObject(hMutexPtrParam, INFINITE);//Wait for the mutex to be triggered, wait until reset, set it immediately
	}
	// Ensure that all child threads have finished running
	WaitForMultipleObjects(10, hThreads, TRUE, INFINITE);
	for(int i = 0; i < 10; i++){
		CloseHandle(hThreads[i]);//Close the thread handle and decrease its kernel object count by one
	}


	//Destroy the mutex and key segment  
	CloseHandle(hMutexPtrParam);
	DeleteCriticalSection(&csGlobVar);
}

It can be seen that, similar to the key segment, the mutex cannot solve the synchronization problem between threads.

       It is thought that the key segment will record the thread ID , that is, "thread ownership", and the mutex also records the thread ID . Could it be that it also has the term "thread ownership".

       The answer is indeed, the mutex also has the concept of "thread ownership". "Thread ownership" is explained in detail in the key paragraph , so I won't repeat it here. In addition, since the mutex is often used for thread mutual exclusion between multiple processes, it has one more useful feature than the key segment - the handling of " abandonment " situations. For example, if a thread occupying a mutex terminates unexpectedly before calling ReleaseMutex () to trigger the mutex (equivalent to the mutex being "abandoned"), will all threads waiting for this mutex be The mutex cannot be triggered and is stuck in an infinite wait process? This is obviously unreasonable. Since the thread holding a mutex terminates, it is enough to prove that it no longer uses the resources protected by the mutex, so these resources are completely and should be used by other threads. Therefore, in this " abandoned " situation, the system automatically sets the thread ID inside the mutex to 0 , and resets its recursion counter to 0 , indicating that the mutex is triggered. The system will then " fairly " select a waiting thread to complete the scheduling ( WaitForSingleObject () of the selected thread will return WAIT_ABANDONED_0 ).



Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324817786&siteId=291194637