windows线程同步与互斥

1、同步与互斥

  • 互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。
  • 同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。

2、windows下的线程互斥

  • 下面介绍下windows下实现线程互斥的方法,分别是临界区对象事件内核对象互斥量信号量

2.1、临界区对象

实现步骤

  • 全局
  • 定义一个全局变量
  • 主线程中
  • 初始化临界区对象
  • 创建线程
  • 等待线程运行结束
  • 删除临界区对象
  • 关闭线程句柄
  • 子线程中
  • 申请进入临界区
  • 进行相关工作
  • 离开临界区

编码实现

#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;
}

运行结果

  • 从运行结果可以看到,这三个线程之间实现了互斥,即一个线程执行完另一个线程才会执行
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
请按任意键继续. . .
  • 如果我们删除对线程的互斥控制,再看下执行结果,可以很清楚的看出区别
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、事件内核对象

实现步骤

  • 全局
  • 定义一个全局变量
  • 主线程中
  • 创建自动重置,未受信的事件内核对象
  • 创建线程
  • 触发事件
  • 等待线程运行结束
  • 关闭内核对象
  • 关闭线程句柄
  • 子线程中
  • 等待事件触发
  • 进行相关工作
  • 重新触发事件

编码实现

#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;
}

运行结果

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、互斥量

实现步骤

  • 全局
  • 定义一个互斥量
  • 主线程
  • 创建互斥量
  • 创建线程
  • 等待线程运行结束
  • 关闭线程句柄
  • 删除互斥量
  • 子线程
  • 等待互斥量
  • 释放互斥量

编码实现

#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;
}

运行结果

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、信号量

实现步骤

  • 全局
  • 定义一个信号量
  • 主线程
  • 创建信号量(为触发状态)
  • 创建线程
  • 等待线程运行结束
  • 关闭线程句柄
  • 删除信号量
  • 子线程中
  • 使信号量进入为未触发状态
  • 进行相关工作
  • 使信号量进入触发状态

编码实现

#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;
}

运行结果

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、windows下的线程同步

  • 上面介绍的四种方法都是实现线程互斥,接下来介绍线程同步,即在线程互斥基础上实现有序访问

3.1、信号量

  • 信号量除了可以实现线程互斥,也可以实现线程同步

实现步骤

  • 全局
  • 定义多个信号量
  • 主线程
  • 创建信号量(一个为触发,其他为未触发)
  • 创建线程
  • 等待线程运行结束
  • 关闭线程句柄
  • 删除信号量
  • 子线程中
  • 使线程进入未触发状态
  • 进行相关工作
  • 使线程进入触发状态

编码实现

  • 这里我们控制线程2先执行,再执行线程1,最后执行线程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;
}

运行结果

  • 可以看到,线程2先执行,然后线程1执行,最后线程3执行
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
请按任意键继续. . .

猜你喜欢

转载自blog.csdn.net/new9232/article/details/126314866