Windows thread synchronization and mutual exclusion

1. Synchronization and mutual exclusion

  • Mutual exclusion: refers to a resource that only allows one visitor to access it at the same time, which is unique and exclusive. But mutual exclusion cannot limit the order in which visitors access resources, that is, access is out of order.
  • Synchronization: refers to the sequential access of visitors to resources through other mechanisms on the basis of mutual exclusion (in most cases).

2. Thread mutual exclusion under windows

  • The following introduces the methods of implementing thread mutual exclusion under windows, which are critical section objects , event kernel objects , mutexes , and semaphores

2.1, critical section object

Implementation steps

  • global
  • define a global variable
  • in the main thread
  • Initialize the critical section object
  • create thread
  • Wait for the thread to finish running
  • Delete critical section object
  • close thread handle
  • in child thread
  • Apply to enter the critical area
  • carry out related work
  • leave critical section

Encoding implementation

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

// 定义全局变量
CRITICAL_SECTION g_cs;

unsigned int __stdcall ThreadFun1(PVOID lpParam)
{
	//申请进入临界区
	EnterCriticalSection(&g_cs);

	int number = (int)lpParam;

	//获取当前线程id
	DWORD threadId = GetCurrentThreadId();

	while (number > 0) {
		printf("thread id is %d, number is %d\n", threadId, number--);
		Sleep(1000);
	}

	//离开临界区
	LeaveCriticalSection(&g_cs);
	return 0;
}

unsigned int __stdcall ThreadFun2(PVOID lpParam)
{

	//申请进入临界区
	EnterCriticalSection(&g_cs);

	int number = (int)lpParam;

	//获取当前线程id
	DWORD threadId = GetCurrentThreadId();

	while (number > 0) {
		printf("thread id is %d, number is %d\n", threadId, number--);
		Sleep(1000);
	}

	//离开临界区
	LeaveCriticalSection(&g_cs);
	return 0;
}

unsigned int __stdcall ThreadFun3(PVOID lpParam)
{

	//申请进入临界区
	EnterCriticalSection(&g_cs);

	int number = (int)lpParam;

	//获取当前线程id
	DWORD threadId = GetCurrentThreadId();

	while (number > 0) {
		printf("thread id is %d, number is %d\n", threadId, number--);
		Sleep(1000);
	}

	//离开临界区
	LeaveCriticalSection(&g_cs);
	return 0;
}

int main()
{

	unsigned ThreadID1 = 0;
	unsigned ThreadID2 = 0;
	unsigned ThreadID3 = 0;

	// 初始化临界区对象
	InitializeCriticalSection(&g_cs);


	//创建线程
	HANDLE handle1 = (HANDLE)_beginthreadex(NULL, 0, ThreadFun1, (void*)5, 0, &ThreadID1);
	HANDLE handle2 = (HANDLE)_beginthreadex(NULL, 0, ThreadFun2, (void*)5, 0, &ThreadID2);
	HANDLE handle3 = (HANDLE)_beginthreadex(NULL, 0, ThreadFun3, (void*)5, 0, &ThreadID3);

	printf("main function thread1 id is %d\n", ThreadID1);
	printf("main function thread2 id is %d\n", ThreadID2);
	printf("main function thread3 id is %d\n", ThreadID3);
	

	//永久等待线程运行结束
	WaitForSingleObject(handle1, INFINITE);
	WaitForSingleObject(handle2, INFINITE);
	WaitForSingleObject(handle3, INFINITE);

	//删除临界区对象
	DeleteCriticalSection(&g_cs);

	//关闭线程句柄
	CloseHandle(handle1);
	CloseHandle(handle2);
	CloseHandle(handle3);

	system("pause");

	return 0;
}

operation result

  • As can be seen from the running results, the three threads have achieved mutual exclusion, that is, one thread will not execute until the other thread executes
main function thread1 id is 21072
main function thread2 id is 41576
main function thread3 id is 16796
thread id is 21072, number is 5
thread id is 21072, number is 4
thread id is 21072, number is 3
thread id is 21072, number is 2
thread id is 21072, number is 1
thread id is 41576, number is 5
thread id is 41576, number is 4
thread id is 41576, number is 3
thread id is 41576, number is 2
thread id is 41576, number is 1
thread id is 16796, number is 5
thread id is 16796, number is 4
thread id is 16796, number is 3
thread id is 16796, number is 2
thread id is 16796, number is 1
请按任意键继续. . .
  • If we delete the mutual exclusion control for threads, and then look at the execution results, we can clearly see the difference
main function thread1 id is 13796
main function thread2 id is 41216
main function thread3 id is 7088
thread id is 41216, number is 5
thread id is 13796, number is 5
thread id is 7088, number is 5
thread id is 7088, number is 4
thread id is 13796, number is 4
thread id is 41216, number is 4
thread id is 7088, number is 3
thread id is 13796, number is 3
thread id is 41216, number is 3
thread id is 13796, number is 2
thread id is 41216, number is 2
thread id is 7088, number is 2
thread id is 13796, number is 1
thread id is 7088, number is 1
thread id is 41216, number is 1
请按任意键继续. . .

2.2, event kernel object

Implementation steps

  • global
  • define a global variable
  • in the main thread
  • Create an auto-reset, untrusted event kernel object
  • create thread
  • trigger event
  • Wait for the thread to finish running
  • close kernel object
  • close thread handle
  • in child thread
  • wait for event to trigger
  • carry out related work
  • retrigger event

Encoding implementation

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

// 定义全局变量
HANDLE g_hEvent;

unsigned int __stdcall ThreadFun1(PVOID lpParam)
{
	//等待事件触发
	WaitForSingleObject(g_hEvent, INFINITE);

	int number = (int)lpParam;

	//获取当前线程id
	DWORD threadId = GetCurrentThreadId();

	while (number > 0) {
		printf("thread id is %d, number is %d\n", threadId, number--);
		Sleep(1000);
	}

	//重新触发事件
	SetEvent(g_hEvent);
	return 0;
}

unsigned int __stdcall ThreadFun2(PVOID lpParam)
{
	//等待事件触发
	WaitForSingleObject(g_hEvent, INFINITE);

	int number = (int)lpParam;

	//获取当前线程id
	DWORD threadId = GetCurrentThreadId();

	while (number > 0) {
		printf("thread id is %d, number is %d\n", threadId, number--);
		Sleep(1000);
	}

	//重新触发事件
	SetEvent(g_hEvent);
	return 0;
}

unsigned int __stdcall ThreadFun3(PVOID lpParam)
{
	//等待事件触发
	WaitForSingleObject(g_hEvent, INFINITE);

	int number = (int)lpParam;

	//获取当前线程id
	DWORD threadId = GetCurrentThreadId();

	while (number > 0) {
		printf("thread id is %d, number is %d\n", threadId, number--);
		Sleep(1000);
	}

	//重新触发事件
	SetEvent(g_hEvent);
	return 0;
}

int main()
{

	unsigned ThreadID1 = 0;
	unsigned ThreadID2 = 0;
	unsigned ThreadID3 = 0;

	// 创建自动重置,未受信的事件内核对象
	g_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);


	//创建线程
	HANDLE handle1 = (HANDLE)_beginthreadex(NULL, 0, ThreadFun1, (void*)5, 0, &ThreadID1);
	HANDLE handle2 = (HANDLE)_beginthreadex(NULL, 0, ThreadFun2, (void*)5, 0, &ThreadID2);
	HANDLE handle3 = (HANDLE)_beginthreadex(NULL, 0, ThreadFun3, (void*)5, 0, &ThreadID3);

	printf("main function thread1 id is %d\n", ThreadID1);
	printf("main function thread2 id is %d\n", ThreadID2);
	printf("main function thread3 id is %d\n", ThreadID3);
	
	//触发事件
	SetEvent(g_hEvent);

	//永久等待线程运行结束
	WaitForSingleObject(handle1, INFINITE);
	WaitForSingleObject(handle2, INFINITE);
	WaitForSingleObject(handle3, INFINITE);

	//关闭内核对象
	CloseHandle(g_hEvent);

	//关闭线程句柄
	CloseHandle(handle1);
	CloseHandle(handle2);
	CloseHandle(handle3);

	system("pause");

	return 0;
}

operation result

main function thread1 id is 43624
main function thread2 id is 42564
main function thread3 id is 43648
thread id is 43624, number is 5
thread id is 43624, number is 4
thread id is 43624, number is 3
thread id is 43624, number is 2
thread id is 43624, number is 1
thread id is 42564, number is 5
thread id is 42564, number is 4
thread id is 42564, number is 3
thread id is 42564, number is 2
thread id is 42564, number is 1
thread id is 43648, number is 5
thread id is 43648, number is 4
thread id is 43648, number is 3
thread id is 43648, number is 2
thread id is 43648, number is 1
请按任意键继续. . .

2.3. Mutex

Implementation steps

  • global
  • define a mutex
  • main thread
  • create mutex
  • create thread
  • Wait for the thread to finish running
  • close thread handle
  • delete mutex
  • child thread
  • wait for mutex
  • release the mutex

Encoding implementation

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

// 定义互斥量
HANDLE hMutex;

unsigned int __stdcall ThreadFun1(PVOID lpParam)
{
	//等待互斥量
	WaitForSingleObject(hMutex, INFINITE);

	int number = (int)lpParam;

	//获取当前线程id
	DWORD threadId = GetCurrentThreadId();

	while (number > 0) {
		printf("thread id is %d, number is %d\n", threadId, number--);
		Sleep(1000);
	}

	//释放互斥量 
	ReleaseMutex(hMutex);
	return 0;
}

unsigned int __stdcall ThreadFun2(PVOID lpParam)
{
	//等待互斥量
	WaitForSingleObject(hMutex, INFINITE);

	int number = (int)lpParam;

	//获取当前线程id
	DWORD threadId = GetCurrentThreadId();

	while (number > 0) {
		printf("thread id is %d, number is %d\n", threadId, number--);
		Sleep(1000);
	}

	//释放互斥量 
	ReleaseMutex(hMutex);
	return 0;
}

unsigned int __stdcall ThreadFun3(PVOID lpParam)
{
	//等待互斥量
	WaitForSingleObject(hMutex, INFINITE);

	int number = (int)lpParam;

	//获取当前线程id
	DWORD threadId = GetCurrentThreadId();

	while (number > 0) {
		printf("thread id is %d, number is %d\n", threadId, number--);
		Sleep(1000);
	}

	//释放互斥量 
	ReleaseMutex(hMutex);
	return 0;
}

int main()
{

	unsigned ThreadID1 = 0;
	unsigned ThreadID2 = 0;
	unsigned ThreadID3 = 0;

	// 创建互斥量
	hMutex = CreateMutex(NULL, FALSE, NULL);


	//创建线程
	HANDLE handle1 = (HANDLE)_beginthreadex(NULL, 0, ThreadFun1, (void*)5, 0, &ThreadID1);
	HANDLE handle2 = (HANDLE)_beginthreadex(NULL, 0, ThreadFun2, (void*)5, 0, &ThreadID2);
	HANDLE handle3 = (HANDLE)_beginthreadex(NULL, 0, ThreadFun3, (void*)5, 0, &ThreadID3);

	printf("main function thread1 id is %d\n", ThreadID1);
	printf("main function thread2 id is %d\n", ThreadID2);
	printf("main function thread3 id is %d\n", ThreadID3);
	

	//永久等待线程运行结束
	WaitForSingleObject(handle1, INFINITE);
	WaitForSingleObject(handle2, INFINITE);
	WaitForSingleObject(handle3, INFINITE);

	//关闭线程句柄
	CloseHandle(handle1);
	CloseHandle(handle2);
	CloseHandle(handle3);

	//删除互斥量
	CloseHandle(hMutex);

	system("pause");

	return 0;
}

operation result

main function thread1 id is 42112
main function thread2 id is 43136
main function thread3 id is 9128
thread id is 42112, number is 5
thread id is 42112, number is 4
thread id is 42112, number is 3
thread id is 42112, number is 2
thread id is 42112, number is 1
thread id is 43136, number is 5
thread id is 43136, number is 4
thread id is 43136, number is 3
thread id is 43136, number is 2
thread id is 43136, number is 1
thread id is 9128, number is 5
thread id is 9128, number is 4
thread id is 9128, number is 3
thread id is 9128, number is 2
thread id is 9128, number is 1
请按任意键继续. . .

2.4, signal amount

Implementation steps

  • global
  • define a semaphore
  • main thread
  • Create a semaphore (for the trigger state)
  • create thread
  • Wait for the thread to finish running
  • close thread handle
  • delete semaphore
  • in child thread
  • puts the semaphore into untriggered state
  • carry out related work
  • Put the semaphore into the triggered state

Encoding implementation

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

// 定义信号量
HANDLE hSemap;

unsigned int __stdcall ThreadFun1(PVOID lpParam)
{
	//使信号量进入未触发状态
	WaitForSingleObject(hSemap, INFINITE);

	int number = (int)lpParam;

	//获取当前线程id
	DWORD threadId = GetCurrentThreadId();

	while (number > 0) {
		printf("thread id is %d, number is %d\n", threadId, number--);
		Sleep(1000);
	}

	//使信号量进入触发状态
	ReleaseSemaphore(hSemap, 1, NULL);
	return 0;
}

unsigned int __stdcall ThreadFun2(PVOID lpParam)
{
	//使信号量进入未触发状态
	WaitForSingleObject(hSemap, INFINITE);

	int number = (int)lpParam;

	//获取当前线程id
	DWORD threadId = GetCurrentThreadId();

	while (number > 0) {
		printf("thread id is %d, number is %d\n", threadId, number--);
		Sleep(1000);
	}

	//使信号量进入触发状态
	ReleaseSemaphore(hSemap, 1, NULL);
	return 0;
}

unsigned int __stdcall ThreadFun3(PVOID lpParam)
{
	//使信号量进入未触发状态
	WaitForSingleObject(hSemap, INFINITE);

	int number = (int)lpParam;

	//获取当前线程id
	DWORD threadId = GetCurrentThreadId();

	while (number > 0) {
		printf("thread id is %d, number is %d\n", threadId, number--);
		Sleep(1000);
	}

	//使信号量进入触发状态
	ReleaseSemaphore(hSemap, 1, NULL);
	return 0;
}

int main()
{

	unsigned ThreadID1 = 0;
	unsigned ThreadID2 = 0;
	unsigned ThreadID3 = 0;

	// 创建触发状态的信号量
	hSemap = CreateSemaphore(NULL, 1, 1, NULL);


	//创建线程
	HANDLE handle1 = (HANDLE)_beginthreadex(NULL, 0, ThreadFun1, (void*)5, 0, &ThreadID1);
	HANDLE handle2 = (HANDLE)_beginthreadex(NULL, 0, ThreadFun2, (void*)5, 0, &ThreadID2);
	HANDLE handle3 = (HANDLE)_beginthreadex(NULL, 0, ThreadFun3, (void*)5, 0, &ThreadID3);

	printf("main function thread1 id is %d\n", ThreadID1);
	printf("main function thread2 id is %d\n", ThreadID2);
	printf("main function thread3 id is %d\n", ThreadID3);
	

	//永久等待线程运行结束
	WaitForSingleObject(handle1, INFINITE);
	WaitForSingleObject(handle2, INFINITE);
	WaitForSingleObject(handle3, INFINITE);

	//关闭线程句柄
	CloseHandle(handle1);
	CloseHandle(handle2);
	CloseHandle(handle3);

	//删除信号量
	CloseHandle(hSemap);

	system("pause");

	return 0;
}

operation result

main function thread1 id is 25020
main function thread2 id is 33360
main function thread3 id is 20720
thread id is 25020, number is 5
thread id is 25020, number is 4
thread id is 25020, number is 3
thread id is 25020, number is 2
thread id is 25020, number is 1
thread id is 33360, number is 5
thread id is 33360, number is 4
thread id is 33360, number is 3
thread id is 33360, number is 2
thread id is 33360, number is 1
thread id is 20720, number is 5
thread id is 20720, number is 4
thread id is 20720, number is 3
thread id is 20720, number is 2
thread id is 20720, number is 1
请按任意键继续. . .

3. Thread synchronization under windows

  • The four methods described above are all to achieve thread mutual exclusion. Next, thread synchronization is introduced, that is, orderly access is realized on the basis of thread mutual exclusion.

3.1, signal amount

  • In addition to realizing thread mutual exclusion, semaphore can also realize thread synchronization

Implementation steps

  • global
  • Define multiple semaphores
  • main thread
  • Create a semaphore (one is triggered, the other is not triggered)
  • create thread
  • Wait for the thread to finish running
  • close thread handle
  • delete semaphore
  • in child thread
  • Put the thread into the untriggered state
  • carry out related work
  • Put the thread into the triggered state

Encoding implementation

  • Here we control thread 2 to execute first, then execute thread 1, and finally execute thread 3
#include <stdio.h>
#include <windows.h>
#include <process.h>

// 定义信号量
HANDLE hSemap1;
HANDLE hSemap2;
HANDLE hSemap3;

unsigned int __stdcall ThreadFun1(PVOID lpParam)
{
	//使信号量1进入未触发状态
	WaitForSingleObject(hSemap1, INFINITE);

	int number = (int)lpParam;

	//获取当前线程id
	DWORD threadId = GetCurrentThreadId();

	while (number > 0) {
		printf("thread id is %d, number is %d\n", threadId, number--);
		Sleep(1000);
	}

	//使信号量3进入触发状态
	ReleaseSemaphore(hSemap3, 1, NULL);
	return 0;
}

unsigned int __stdcall ThreadFun2(PVOID lpParam)
{
	//使信号量2进入未触发状态
	WaitForSingleObject(hSemap2, INFINITE);

	int number = (int)lpParam;

	//获取当前线程id
	DWORD threadId = GetCurrentThreadId();

	while (number > 0) {
		printf("thread id is %d, number is %d\n", threadId, number--);
		Sleep(1000);
	}

	//使信号量1进入触发状态
	ReleaseSemaphore(hSemap1, 1, NULL);
	return 0;
}

unsigned int __stdcall ThreadFun3(PVOID lpParam)
{
	//使信号量3进入未触发状态
	WaitForSingleObject(hSemap3, INFINITE);

	int number = (int)lpParam;

	//获取当前线程id
	DWORD threadId = GetCurrentThreadId();

	while (number > 0) {
		printf("thread id is %d, number is %d\n", threadId, number--);
		Sleep(1000);
	}

	return 0;
}

int main()
{

	unsigned ThreadID1 = 0;
	unsigned ThreadID2 = 0;
	unsigned ThreadID3 = 0;


	// 设置信号量1的状态为未触发状态
	hSemap1 = CreateSemaphore(NULL, 0, 1, NULL);
	// 设置信号量2的状态为触发状态
	hSemap2 = CreateSemaphore(NULL, 1, 1, NULL);
	// 设置信号量3的状态为未触发状态
	hSemap3 = CreateSemaphore(NULL, 0, 1, NULL);


	//创建线程
	HANDLE handle1 = (HANDLE)_beginthreadex(NULL, 0, ThreadFun1, (void*)5, 0, &ThreadID1);
	HANDLE handle2 = (HANDLE)_beginthreadex(NULL, 0, ThreadFun2, (void*)5, 0, &ThreadID2);
	HANDLE handle3 = (HANDLE)_beginthreadex(NULL, 0, ThreadFun3, (void*)5, 0, &ThreadID3);

	printf("main function thread1 id is %d\n", ThreadID1);
	printf("main function thread2 id is %d\n", ThreadID2);
	printf("main function thread3 id is %d\n", ThreadID3);
	

	//永久等待线程运行结束
	WaitForSingleObject(handle1, INFINITE);
	WaitForSingleObject(handle2, INFINITE);
	WaitForSingleObject(handle3, INFINITE);

	//关闭线程句柄
	CloseHandle(handle1);
	CloseHandle(handle2);
	CloseHandle(handle3);

	//删除信号量
	CloseHandle(hSemap1);
	CloseHandle(hSemap2);
	CloseHandle(hSemap3);

	system("pause");

	return 0;
}

operation result

  • It can be seen that thread 2 executes first, then thread 1 executes, and finally thread 3 executes
main function thread1 id is 41360
main function thread2 id is 42024
main function thread3 id is 43380
thread id is 42024, number is 5
thread id is 42024, number is 4
thread id is 42024, number is 3
thread id is 42024, number is 2
thread id is 42024, number is 1
thread id is 41360, number is 5
thread id is 41360, number is 4
thread id is 41360, number is 3
thread id is 41360, number is 2
thread id is 41360, number is 1
thread id is 43380, number is 5
thread id is 43380, number is 4
thread id is 43380, number is 3
thread id is 43380, number is 2
thread id is 43380, number is 1
请按任意键继续. . .

Guess you like

Origin blog.csdn.net/new9232/article/details/126314866
Recommended