C++——线程实现同步的4种方式(信号量、互斥对象、事件、临界区)

#include <stdio.h>
#include <process.h>
#include <Windows.h>
#include <iostream>
#include <chrono>
#include <iomanip>

// 线程数量
#define THREAD_NUM 10

HANDLE* handles = new HANDLE[THREAD_NUM];
unsigned long long ticketNum = 100000;
/*
CreateMutexA(
	_In_opt_ LPSECURITY_ATTRIBUTES lpMutexAttributes, 安全属性、如果此参数为 NULL,则互斥体将使用默认的安全描述符。
	_In_ BOOL bInitialOwner, 如果此参数为 TRUE,则创建互斥体的线程将成为其初始所有者。这意味着该线程将拥有对同步资源的独占访问权,并且互斥体将处于未触发状态(即其他线程无法访问)。如果此参数为 FALSE,则互斥体在创建时不被任何线程拥有,并且处于触发状态(即其他线程可以调用等待函数来尝试获取它)
	_In_opt_ LPCSTR lpName、这是一个指向字符串的指针,用于指定互斥体的名称。
	);
*/
HANDLE hmutex = CreateMutex(NULL, FALSE, NULL);

/*
CreateEventA(
	_In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes,安全属性、如果此参数为 NULL,则互斥体将使用默认的安全描述符。
	_In_ BOOL bManualReset,指定事件对象是否为手动重置。如果为 `TRUE`,则需要使用 `ResetEvent` 函数来手动将事件状态重置为无信号状态。如果为 `FALSE`,则系统会自动在单个等待线程被释放后重置事件状态
	_In_ BOOL bInitialState,指定事件对象的初始状态。如果为 `TRUE`,则初始状态为有信号状态;如果为 `FALSE`,则为无信号状态
	_In_opt_ LPCSTR lpName,指定事件对象的名称。如果为 `NULL`,则创建一个匿名的事件对象
	);
*/
HANDLE hevent = CreateEventA(NULL, FALSE, TRUE, NULL);
/*
CreateSemaphoreA(
	_In_opt_ LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,安全属性、如果此参数为 NULL,则互斥体将使用默认的安全描述符。
	_In_     LONG lInitialCount,这个参数指定信号量的初始计数。它表示可以同时访问共享资源的线程数。
	_In_     LONG lMaximumCount,这个参数指定信号量的最大计数。它表示可以访问共享资源的最大线程数。
	_In_opt_ LPCSTR lpName,指定事件对象的名称。如果为 `NULL`,则创建一个匿名的事件对象
	);
*/
HANDLE hsemaphore = CreateSemaphoreA(NULL, 1, 1, NULL);

// 临界区,使用临界区实现同步功能
CRITICAL_SECTION g_cs;

// 使用互斥体去卖票、总花费时间:12608ms
unsigned long WINAPI HandleMutexMethod(void* arg)
{
	int* i = (int*)arg;
	while (ticketNum > 0)
	{
		WaitForSingleObject(hmutex, INFINITE);
		if (ticketNum > 0)
		{
			ticketNum--;
			printf("%d成功卖出一张票。余票为:%lld\n", *i, ticketNum);
		}
		else {
			ReleaseMutex(hmutex);
			break;
		}
		ReleaseMutex(hmutex);
	}
	printf("%d票卖完了。余票为:%lld\n", *i, ticketNum);
	delete arg;
	return 0;
}

// 使用事件去卖票、总花费时间:12175ms
unsigned long WINAPI HandleEventMethod(void* arg) 
{
	int* i = (int*)arg;
	while (ticketNum > 0)
	{
		WaitForSingleObject(hevent, INFINITE);
		if (ticketNum > 0)
		{
			ticketNum--;
			printf("%d成功卖出一张票。余票为:%lld\n", *i, ticketNum);
		}
		else {
			SetEvent(hevent);
			break;
		}
		SetEvent(hevent);
	}
	printf("%d票卖完了。余票为:%lld\n", *i, ticketNum);
	delete arg;
	return 0;
}

// 使用信号量来卖票、总花费时间:11226ms
unsigned long WINAPI HandleSemapMethod(void* arg)
{
	int* i = (int*)arg;
	while (ticketNum > 0)
	{
		WaitForSingleObject(hsemaphore, INFINITE);
		if (ticketNum > 0)
		{
			ticketNum--;
			printf("%d成功卖出一张票。余票为:%lld\n", *i, ticketNum);
		}
		else {
			// 让信号量+1
			ReleaseSemaphore(hsemaphore, 1, NULL);
			break;
		}
		ReleaseSemaphore(hsemaphore, 1, NULL);
	}
	printf("%d票卖完了。余票为:%lld\n", *i, ticketNum);
	delete arg;
	return 0;
}

// 使用临界区实现卖票同步,总花费时间:10423ms
unsigned long WINAPI HandleCriticalMethod(void* arg)
{
	int* i = (int*)arg;
	while (ticketNum > 0)
	{
		// 进入临界区
		EnterCriticalSection(&g_cs);
		if (ticketNum > 0)
		{
			ticketNum--;
			printf("%d成功卖出一张票。余票为:%lld\n", *i, ticketNum);
		}
		else {
			// 离开临界区
			LeaveCriticalSection(&g_cs);
			break;
		}
		LeaveCriticalSection(&g_cs);
	}
	printf("%d票卖完了。余票为:%lld\n", *i, ticketNum);
	delete arg;
	return 0;
}

// 互斥对象用来控制线程同步问题
int main()
{
	// 获取当前时间
	auto now = std::chrono::high_resolution_clock::now();
	auto duration = now.time_since_epoch();
	long long beginNanoSec = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();

	/*
	* CreateThread参数解释
	* CreateThread(
	_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, 安全属性,通常为NULL
	_In_ SIZE_T dwStackSize, 新线程的初始栈大小(以字节为单位),写0的话默认为1M
	_In_ LPTHREAD_START_ROUTINE lpStartAddress, 要执行的方法的地址
	_In_opt_ __drv_aliasesMem LPVOID lpParameter, 传入方法中的参数
	_In_ DWORD dwCreationFlags, 控制线程创建的标志。这可以是一个或多个值的组合,如 0(表示没有特殊标志)、CREATE_SUSPENDED(表示创建线程后挂起它,直到调用 ResumeThread)、STACK_SIZE_PARAM_IS_A_RESERVATION(表示 dwStackSize 参数指定了栈大小的保留大小,而不是提交大小)等。
	_Out_opt_ LPDWORD lpThreadId 指向一个 DWORD 的指针,该 DWORD 接收新线程的线程标识符
	);
	*/
	// 初始化临界区
	InitializeCriticalSection(&g_cs);
	// 创建100个线程去卖100亿张票
	for (int i = 0; i < THREAD_NUM; i++)
	{
		int* index = new int;
		*index = i;
		handles[i] = CreateThread(NULL, 0, HandleCriticalMethod, index, 0, NULL);
	}
	
	// 等待所有的线程都执行完成
	unsigned long ret = WaitForMultipleObjects(THREAD_NUM, handles, TRUE, INFINITE);
	printf("WaitForMultipleObjects result: %ld\n", ret);

	// 关闭所有句柄
	for (int i = 0; i < THREAD_NUM; i++)
	{
		CloseHandle(handles[i]);
	}
	CloseHandle(hmutex);
	CloseHandle(hevent);
	CloseHandle(hsemaphore);
	// 删除临界区
	DeleteCriticalSection(&g_cs);
	
	// 获取结束时间
	now = std::chrono::high_resolution_clock::now();
	duration = now.time_since_epoch();
	long long endNanoSec = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();

	// 计算使用时间
	long long useTime = endNanoSec - beginNanoSec;
	printf("买票一共花了%lld毫秒\n", useTime);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值