WindowsAPI 查阅笔记:线程、多个线程互同步

news2024/9/22 7:30:09

1. 线程的创建

HANDLE CreateThread(
	LPSECURITY_ATTRIBUTES 	lpThreadAttributes, //线程安全属性 
	SIZE_T					dwStackSize,		//线程堆栈大小 
	LPTHREAD_START_ROUTINE 	lpStartAddress,	//重要:  线程函数指针 
	LPVOID 					lpParameter,	//重要:  启动线程函数 
	DWORD 					dwCreationFlags,	//线程安全属性
	LPDWORD 				lpThreadId			//返回 TID 
);//返回新建线程的句柄 

1.1 线程函数

要符合接口参数要求

//ThreadProc 函数原型
DWORD WINAPI ThreadProc(
	LPVOID lpParameter
);

1.2 线程句柄与 TID

每一个线程都有一个句柄和一个标识符 (TID)。
TID 是 DWORD 类型,每个线程的 TID 都不同,所以可以用 TID 标识唯一的 线程。

  • 通过 TID 和 OpenThread() 可以获取线程的句柄。
  • 通过 句柄 和 GetThreadId()可以获取线程的TID。
  • GetCurrentThread() 获取本线程句柄。
  • GetCurrentThreadId() 获取本线程 TID。

1.3 创建线程代码

#include <windows.h>
#include <cstdio>

#define MAX_THREADS 5

typedef struct _THREAD_PARAM{
	DWORD i;
	DWORD dwRandom;
	DWORD dwData;
} THREAD_PARAM, *LPTHREAD_PARAM;

/****************
功能:线程函数,将参数打印出来 
****************/ 
DWORD WINAPI ThreadProc(LPVOID lpParam)
{
	//参数数据类型 
	LPTHREAD_PARAM pData;
	pData = (LPTHREAD_PARAM)lpParam;
	
	printf("TID = %u,\tparameters = %u, %u, %u\n",
		GetCurrentThreadId(),
		pData->i,
		pData->dwRandom,
		pData->dwData);
	//释放保存参数的内存(主线程中分配的)
	HeapFree(GetProcessHeap(), 0, pData); 
	return 0;
}

/*********************
功能:创建多个线程 
******************/
int main(int argc, char **argv)
{
	LPTHREAD_PARAM pData;
	DWORD dwThreadId[MAX_THREADS];
	HANDLE hThread[MAX_THREADS];
	int i;
	//创建 MAX_THREADS 个线程 
	for(i = 0 ;i < MAX_THREADS; ++i){
		//为线程函数参数分配内存
		pData = (LPTHREAD_PARAM)HeapAlloc(GetProcessHeap(),
			HEAP_ZERO_MEMORY, sizeof(THREAD_PARAM));
			
		if(pData == NULL){
			printf("HeapAlloc error;\n");
			ExitProcess(2);
		} 
		//设置参数
		pData->i = i;
		pData->dwRandom = rand();
		pData->dwData = 100;
		//创建线程
		hThread[i] = CreateThread(
			NULL,		//默认安全属性
			0,			//默认堆栈大小
			ThreadProc,	//线程函数
			pData,		//参数
			0,			//默认创建标志
			&dwThreadId[i]);//返回TID 
			
		if(hThread[i] == NULL){
			printf("hThread[%d]创建失败!\n",i);
			ExitProcess(i);
		}
		Sleep(10);
	}
	
	//主线程等待其他子线程执行结束
	WaitForMultipleObjects(MAX_THREADS, hThread, TRUE, INFINITE);
	//关闭所有线程的句柄
	for(i = 0; i < MAX_THREADS; ++i){
		CloseHandle(hThread[i]);
	} 
	return 0;
}



代码运行结果:
在这里插入图片描述

2. 线程的挂起、恢复、切换、终止

//schedule.cpp

#include <windows.h>
#include <cstdio>

DWORD WINAPI ThreadProc(LPVOID lpParam)
{
	LPDWORD pData = (LPDWORD)lpParam;
	DWORD i = 0;
	
	for(i = 0; i < 10; ++i){
		Sleep(100);//每 ms 打印一次
		printf("TID = %u,\tparameters = %u\ti = %u\n",
			GetCurrentThreadId(), *pData, i); 
	}
	ExitThread(i);
	return 0;
}
/*********************
功能:线程调度 
*********************/
int main(int argc, char **argv)
{
	DWORD dwData;
	DWORD dwThreadId[2];
	HANDLE hThread[2];
	
	//创建线程
	dwData = 1;
	hThread[0] = CreateThread(
		NULL,0,
		ThreadProc,
		&dwData,
		CREATE_SUSPENDED,	//挂起新建的进程
		&dwThreadId[0] 
	);
	if(hThread[0] == NULL){
		ExitProcess(0); 
	}
	
	//创建线程
	dwData = 2;
	hThread[1] = CreateThread(NULL, 
		0, 
		ThreadProc, 
		&dwData, 
		0,
		&dwThreadId[1]);
	if(hThread[1] == NULL){
		ExitProcess(1);
	} 
	
	//等待 200ms 恢复线程的执行
	Sleep(200);
	ResumeThread(hThread[0]);
	//挂起线程的执行
	SuspendThread(hThread[1]);
	//等待 300 ms 终止线程,恢复线程
	Sleep(300);
	TerminateThread(hThread[0], 0);
	ResumeThread(hThread[1]);
	
	//等待所有线程执行结束
	WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
	//关闭所有线程的句柄
	CloseHandle(hThread[0]);
	CloseHandle(hThread[1]);
	
	return 0;
}

运行结果:
在这里插入图片描述

3. 等待函数

等待函数可以等待同步对象以外,还可以等待其他对象,包括进程和线程。以进程或线程句柄为等待对象就可以了。
以下两个是下面的同步代码中用到的。
在下面代码中用到的时候添加了注释。

3.1 等待函数

//WaitForSingleObject 
//功能是等待单个对象,如果对象被标记,则返回
DWORD WINAPI WaitForSingleObject(
	DANDLE hHandle,
	DWORD dwMilliseconds
);

//WaitForMultipleObjects 
// 功能是等待多个对象,等待的所有对象都设置为标记的 / 有一个设置为标记的 则返回.
DWORD WINAPI WaitForMultipleObjects(
	DWORD nCount,
	const HANDLE *lpHandles,
	BOOL bWaitAll,
	DWORD dwMilliseconds
);

3.2 标记函数

设置标记:

//设置标记
//将事件对象设置为标记
BOOL WINAPI SetEvent(HANDLE hEvent);

重置标记:

//重置标记
//如果事件设置为手工重置,那么需要使用此函数来重置事件
BOOL WINAPI ResetEvent(HANDLE hEvent);

获取事件句柄:

//从事件名中获取事件句柄
HANDLE WINAPI OpenEvent(
	DWORD dwDesiredAccess,
	BOOL bInheritHandle,
	LPCTSTR lpName
);

4. 多个线程互同步

创建三个线程,对一个全局变量进行读操作。
通过三个读事件和写事件控制三个线程,避免在写入数据的时候发生【读事件】。

  1. 主线程创建3个线程,创建【读事件】和【写事件】,并设置为false.
  2. 主线程开始写入数据,之后打开【写事件】,并开始等待【读事件】。
  3. 子线程之前的【写事件】状态是关闭,所以在等待。【写事件】打开后,开始执行打印数据操作。然后打开【读事件】。
  4. 最后关闭子进程。
#include <windows.h>
#include <cstdio>

//常量定义 
#define NUMTHREADS 3
#define BUFFER_SIZE 16
#define FOR_TIMES 5

//全局变量
HANDLE hThread[NUMTHREADS]; 
//写 event 表示写操作是否完成 
HANDLE hWriteEvent[NUMTHREADS];
//读 event 表示读操作是否完成 
HANDLE hReadEvents[NUMTHREADS]; 
//共享内存 
// BYTE lpSharedBuffer[16] = {0};
LPSTR lpSharedBuffer;
DWORD* index;

//函数声明
void MultiEvents(void);
void WriteToBuffer(void);
DWORD WINAPI ThreadFunction(LPVOID lpParam);
/******************
int main(void)
******************/ 
int main(void)
{
	index = (DWORD*)malloc(sizeof(DWORD));
	lpSharedBuffer = (char*)malloc(sizeof(char)*64);
	
	MultiEvents();
	
	free(index);
	free(lpSharedBuffer);
	return 0;
}
/*******************
演示 event 的使用方法 
******************/
void MultiEvents(void)
{
//	HANDLE hThread;
	DWORD i = 0;
	
	*index = i;

	//创建多个线程,读共享内存,主线程写共享内存
	//每个线程都有对应的读写同步事件
	for(i = 0; i < NUMTHREADS; ++i){
		*index = i;
		//每个线程都有一个 event 表示写入操作完成
		hWriteEvent[i] = CreateEvent(
			NULL,		//默认安全属性
			FALSE,		//自动重置
			FALSE,		//初始化为未置位的
			NULL		//未命名 
		);
		//判断是否创建成功
		if(hWriteEvent[i] == NULL){
			printf("CreateEvent failed (%d)\n",GetLastError());
			return ;
		} 
		
		//每个线程都有一个 event 表示读入操作完成
		hReadEvents[i] = CreateEvent(
			NULL,		//默认安全属性
			FALSE,		//自动重置
			FALSE,		//初始化为未置位的
			NULL		//未命名 
		);
		//判断是否创建成功
		if(hReadEvents[i] == NULL){
			printf("CreateEvent failed (%d)\n",GetLastError());
			return ;
		} 
		
		
		//创建线程

		hThread[i] = CreateThread(NULL, 0, 
			ThreadFunction, 
			index, 
			0, NULL
		);
			
		if(hThread[i] == NULL){
			printf("CreateThread failed (%d)\n",GetLastError());
			return ;
		} 
		printf("CreateThread: %d\n",i);

	} 
	WriteToBuffer();
	
	
	//主线程等待其他子线程执行结束
	WaitForMultipleObjects(NUMTHREADS, 
		hThread, TRUE, INFINITE);
	//关闭所有线程的句柄
	for(i = 0; i < NUMTHREADS; ++i){
		CloseHandle(hThread[i]);
		printf("CloseThread:%d\n",i);
	} 
	

	return ;
}

/************************
由主线程调用,向共享内存中写入数据,
等待所有读线程读完后函数返回 
*********************/
void WriteToBuffer(void)
{
	DWORD dwWaitResult, j, i;
	//完成 for_times 次读写
	for(j = 0;j < FOR_TIMES; ++j){
		//写入需要的时间间隔 
		Sleep(rand()%100);
		//写入共享内存
		wsprintf(lpSharedBuffer,"shared %d",j);
		//将线程对应的写 Event 置为 "标志的",表示写操作完成
		//其他线程可以开始读
		for(i = 0; i < NUMTHREADS; ++i){
			if(! SetEvent(hWriteEvent[i])){
				printf("SetEvent failed (%d)\n",GetLastError());
				return ;
			}
		} 
		dwWaitResult = WaitForMultipleObjects(
			NUMTHREADS,		//Event 句柄的个数
			hReadEvents,	//Event句柄数组
			TRUE,			//等到所有的event都被标志
			INFINITE 		//无线等待 
		);
		//判断等待结果
		if(dwWaitResult != WAIT_OBJECT_0){
			printf("Wait errpr (%d)\n",GetLastError());
			ExitProcess(0);
		} 
	} 
	return ;
} 

/*******************
线程函数,读共享内存 
*******************/
DWORD WINAPI ThreadFunction(LPVOID lpParam)
{
	DWORD dwWaitResult;
	LPSTR lpRead[16];
	DWORD j = 0;
	DWORD dwThreadIndex = *(DWORD*)lpParam;
	printf("Hello! dwThreadIndex = %d\n",dwThreadIndex);
	//完成 FOR_TIMES 次读写
	for(; j < FOR_TIMES; ++j)
	{
		//等待写事件置位,表示数据已经写入
		dwWaitResult =  WaitForSingleObject(
			hWriteEvent[dwThreadIndex],	//event 句柄 
			INFINITE					//无限等待 
		);
		
		switch(dwWaitResult)
		{
			case WAIT_OBJECT_0:
					//模拟数据处理需要的时间间隔 
				Sleep(rand()%10);
				CopyMemory(lpRead, lpSharedBuffer, 16);
				break;
				//发生错误
			default:
				printf("wait errpr: %d\n",GetLastError());
				ExitProcess(0);
		}
		//将读 event 置位,表示读操作完成
		if(!SetEvent(hReadEvents[dwThreadIndex])){
			printf("SetEvent failed (%d)\n",GetLastError());
			return 0;
		} 
		//打印读到的内容
		printf("线程 %u\t第 %d 次读, 内容: %s\n",
			dwThreadIndex,j,(LPSTR)lpRead);
	} 
	return 1;
}

运行结果:
在这里插入图片描述

5. C++11 中的互斥

#include <iostream>
#include <thread>
#include <mutex>
#include <windows.h>

std::mutex mtx;	//全局互斥锁
int shared_resource = 0;

void Num1(void){
	for(int i = 0; i < 3; ++i){
		mtx.lock();//锁定互斥锁
		++shared_resource;
		printf("This is AddNum!\n");
		mtx.unlock();//解锁互斥锁 
	}
	return ;
}
void Num2(void){
	for(int i = 0; i < 3; ++i){
		mtx.lock();
		++shared_resource;
		printf("This is PrintNum!\n");
		mtx.unlock();
	}
	return ;
}

int main(void){
	
	std::thread t1(Num1);
	std::thread t2(Num2);
	
	t1.join();
	t2.join();
	
	std::cout <<"Final value of shared_resource: " << shared_resource << std::endl;
	
	return 0;
}

运行结果:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2037167.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

分布式知识总结(一致性Hash算法)

文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 一致性Hash算法 假如有三台服务器编号node0、node1、node2&…

若依项目改造-sqlserver(2)

目前启动时&#xff0c;不需要配置数据库 1、启动时&#xff0c;不用检查数据库连接 2、可以在线导出数据库备份bak文件 3、实现不用配置jdk环境启动 4、实现启动没有控制台窗口

排序【归并排序和计数排序】

1.归并排序 1.1 基本思想 并归排序&#xff1a;是建立在归并操作上的一种有效的排序算法,该算法是采用分治法&#xff08;Divide and Conquer&#xff09;的一个非常典型的应用。将已有序的子序列合并&#xff0c;得到完全有序的序列&#xff1b;即先使每个子序列有序&#x…

【代理模式】设计模式系列:实现与最佳实践(掌控访问的艺术)

文章目录 Java中的代理模式引言1. 代理模式概念1.1 代理模式定义1.2 代理模式的参与者1.3 代理模式的基本工作原理1.4 代理模式的优点与缺点 2. Java代理模式实现方式2.1 静态代理2.2 动态代理2.2.1 JDK动态代理2.2.2 CGLIB动态代理 2.3 两者之间的区别与选择 3. 使用案例分析3…

Mariadb数据库本机无密码登录的问题解决

Mariadb数据库本机无密码登录的问题解决 安装了mariadb后&#xff0c;发现Mariadb本机无密码才能登录 百度了很多文章&#xff0c;发现很多人是因为root的plugin设置的值不正确导致的&#xff0c;unix_socket可以不需要密码&#xff0c;mysql_native_password 是正常的。 解…

Codeforces Round 965 (Div. 2)

前言 有人在过七夕&#xff0c;我在打 cf &#xff0c;还有某人独自一人在学校机房&#xff0c;凌晨一点骑上共享单车回宿舍欣赏沿途的秋风扫落叶。 Standings&#xff1a;2166 题目链接&#xff1a;Dashboard - Codeforces Round 965 (Div. 2) - Codeforces A. Find K Distin…

未来能源技术

未来能源技术正处于全球焦点的中心&#xff0c;旨在应对气候变化、资源枯竭和能源安全的挑战。未来能源技术的发展方向集中在可再生能源、能源储存技术、智能电网、核聚变以及新材料的应用等多个领域。 1. 可再生能源技术 1.1 太阳能技术 太阳能技术是未来能源发展的核心领域之…

精密Δ-Σ ADC的有效噪声带宽

1 简介 即使对最有经验的模拟设计工程师来说&#xff0c;理解ADC噪声也是一项挑战。Δ-Σ ADC具有量化噪声和热噪声&#xff0c;其变化取决于ADC的分辨率、参考电压和输出数据速率。在系统层面上&#xff0c;噪声分析因附加的信号链组件而变得更加复杂&#xff0c;这些组件中的…

NLP_情感分类_序列模型方案

文章目录 项目背景代码导包读取数据文本预处理举例查看分词器数据集调整进一步剖析&#xff1a;对应Step [{i1}/{len(train_loader)}] 里的train_loader进一步剖析&#xff1a;Step [{i1}/{len(train_loader)}] 里的train_loader&#xff0c;原始的train_df 计算数据集中最长文…

Java 并发(四)—— volatile 和 synchronized

一、volatile 关键字 1.概念 如果我们将一个变量使用 volatile 修饰&#xff0c;这就指示 编译器&#xff0c;这个变量是共享且不稳定的&#xff0c;每次使用它都到主存中进行读取。 2.作用 保证变量对所有线程的可见性。但不能保证数据的原子性。因此不能完全保证线程安全…

STP(生成树)的概述和工作原理

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…

PHPStorm 环境配置与应用详解

​ 大家好&#xff0c;我是程序员小羊&#xff01; 前言&#xff1a; PHPStorm 是 JetBrains 出品的一款专业 PHP 集成开发环境&#xff08;IDE&#xff09;&#xff0c;凭借其智能的代码补全、调试功能、深度框架支持和前端开发工具&#xff0c;为用户提供了丰富的功能和工具…

简单的敏感词提示功能

简单的敏感词提示功能 1. 需求 公司现在接到通知&#xff0c;部分接口的部分手动输入字段&#xff0c;需要新增敏感词报红提示&#xff0c;敏感词汇现在应该是7000多个左右&#xff0c;需要我们提供一个敏感词校验接口&#xff0c;如果前端输入敏感词&#xff0c;则前端提示出…

在Unreal Engine中使用C++创建基础角色并添加移动功能

目录 引言 步骤一&#xff1a;创建C类 步骤二&#xff1a;编写C代码 步骤三&#xff1a;设置输入绑定 步骤四&#xff1a;在UE编辑器中测试 结论 引言 Unreal Engine&#xff08;UE&#xff09;以其强大的功能和灵活性在游戏开发界广受好评。本文将指导你如何在UE中通过…

校园外卖平台小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;商家管理&#xff0c;菜品信息管理&#xff0c;菜品分类管理&#xff0c;购买菜品管理&#xff0c;订单信息管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&a…

揭开ChatGPT进化之谜:人工智能如何变得更聪明

近年来&#xff0c;人工智能&#xff08;AI&#xff09;领域取得了显著进展&#xff0c;尤其是在自然语言处理&#xff08;NLP&#xff09;方面。OpenAI的GPT系列模型&#xff0c;如GPT-3和ChatGPT&#xff0c;代表了这一领域的前沿技术。本文将围绕ChatGPT提升的原因、发展趋势…

基于JSP的个性化影片推荐系统

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;JSP 数据库&#xff1a;MySQL 技术&#xff1a;JSP技术 工具&#xff1a;MyEclipse、Tomcat、MySQL 系统展示 首页 管理员功能模块 用户功能模块 …

国外评论家称《黑神话》PC版有性能问题 还有卡顿现象

《黑神话&#xff1a;悟空》即将正式发售&#xff0c;人们对这款游戏的期待值相当高。所以许多粉丝在耐心等待第一批评测报道&#xff0c;然后再购买这款游戏。在8月16日媒体评测解禁之前&#xff0c;有一位国外评论家认为《黑神话&#xff1a;悟空》是一款好游戏&#xff0c;但…

Vue的监视属性watch、computed和watch之间的区别

目录 1. 监视属性2. 监视属性的简写3. computed和watch之间的区别 1. 监视属性 监听对象: 监视属性可以监听普通属性和计算属性调用时间: 当监听的属性发生改变时。handler被调用immediate: true: 是否初始化时让handler调用一下。此时oldVlue为undefineddeep: false: watch默…

美国洛杉矶大带宽服务器的运维与监控

美国洛杉矶的大带宽服务器因其优越的地理位置、高速的网络连接以及充足的带宽资源&#xff0c;在全球范围内享有很高的声誉。为了确保这些服务器的稳定运行和高效服务&#xff0c;运维与监控工作显得尤为重要。以下是一些关于美国洛杉矶大带宽服务器运维与监控方面的科普内容。…