Windows API线程学习

最近无意发现了c语言里可以调用Windows API来实现多线程,对于我这个小白来可是重大发现!让我看看跟pthread有什么区别:D

一.创建线程

cpp 复制代码
// 创建线程的函数原型
​​​​​​​HANDLE CreateThread(
    LPSECURITY_ATTRIBUTES lpThreadAttributes,  // 安全属性(通常为NULL)
    SIZE_T dwStackSize,                         // 栈大小(0表示默认)
    LPTHREAD_START_ROUTINE lpStartAddress,     // 线程函数地址
    LPVOID lpParameter,                         // 传递给线程的参数
    DWORD dwCreationFlags,                      // 创建标志(0表示立即运行)
    LPDWORD lpThreadId                          // 线程ID(可为NULL)
);

测试:

cpp 复制代码
#include <windows.h>
#include <stdio.h>


DWORD WINAPI myFunction(LPVOID lpParam) { //DWORD是线程的返回类型(Double Word),WINAPI为调用约定,LPVOID为指向任何类型的指针(void*)
    printf("111");
    return 0;
}

int main() {
    // 创建线程
    HANDLE hThread = CreateThread(
        NULL,           //安全属性(默认)
        0,              //栈大小(0=默认1MB)
        myFunction,  	//线程函数地址
        NULL,           // 传递给线程的参数
        0,              // 创建标志(0=立即运行)
        NULL            // 线程ID(不需要可以NULL)
    );
    
    
    // 等待线程结束
    WaitForSingleObject(hThread, INFINITE);//INFINITE表示无限等待,填1000表示最多等1秒
    
    // 关闭句柄
    CloseHandle(hThread);
    
    return 0;
}

二.传递参数

cpp 复制代码
#include <windows.h>
#include <stdio.h>

DWORD WINAPI tFunction(LPVOID lpParam){
	int* value = (int*)lpParam;//获取传送的数据
	printf("%d",*value);
	Sleep(1000);//这里等待的时候,tFunction2的内容已经跑完了 ,验证了多线程在正常工作 
	return 0;
}

DWORD WINAPI tFunction2(LPVOID lpParam){
	printf("222");
	return 0;
}

int main(){
	int data=100;
	HANDLE hThread=CreateThread(NULL,0,tFunction,&data,0,NULL);//这里&data就是要传递的参数 
	HANDLE hThread2=CreateThread(NULL,0,tFunction2,NULL,0,NULL);
	WaitForSingleObject(hThread,INFINITE);
	WaitForSingleObject(hThread2,INFINITE);
	CloseHandle(hThread);
	CloseHandle(hThread);
	return 0;
}

附:传递结构体

这个比传递简单数据更实用

cpp 复制代码
#include<windows.h>
#include<stdio.h>

typedef struct{
	int id;
	char name[50];
}mPerson;

DWORD WINAPI tFunction(LPVOID lpParam){
	mPerson* person = (mPerson*)lpParam;
	printf("id:%d\nname:%s",person->id,person->name);
	return 0;
}

int main(){
	mPerson person1={1,"pig"};//初始化
	HANDLE hThread = CreateThread(NULL,0,tFunction,&person1,0,NULL);//传递结构体数据
	WaitForSingleObject(hThread,INFINITE);
	CloseHandle(hThread);
	return 0; 
}

三.互斥锁(Mutex) 重要!

我之前写的一个程序就是少了互斥锁,导致一直出bug,几个线程之间会抢资源

1.创建互斥锁

cpp 复制代码
HANDLE hMutex = CreateMutex(
    NULL,      // 安全属性,填NULL就行
    FALSE,     // 是否初始拥有(FALSE表示初始不锁定)
    NULL       // 名字(NULL表示无名)
);

2.加锁

cpp 复制代码
WaitForSingleObject(hMutex, INFINITE);
// 如果锁被占用,线程会在这里等待直到获得锁

3.解锁

cpp 复制代码
ReleaseMutex(hMutex);
// 释放锁,让其他等待的线程可以进入

4.实战测试

cpp 复制代码
#include<windows.h>
#include<stdio.h>

HANDLE hMutex;//创建互斥锁

DWORD WINAPI print111(LPVOID lpParam){
	WaitForSingleObject(hMutex,INFINITE);//等待
	printf("111\n");
	Sleep(100);
	printf("1111\n");
	Sleep(200);
	printf("11111\n");
	ReleaseMutex(hMutex);//释放锁 
	
	return 0;
}

DWORD WINAPI print222(LPVOID lpParam){
	WaitForSingleObject(hMutex,INFINITE);//等待
	printf("222\n");
	Sleep(100);
	printf("2222\n");
	Sleep(200);
	printf("22222\n");
	ReleaseMutex(hMutex);//释放锁 
	
	return 0;
}


int main(){
	hMutex = CreateMutex(NULL,FALSE,NULL);//FALSE表示初始不锁定
	
	HANDLE hThread1=CreateThread(NULL,0,print111,NULL,0,NULL);
	HANDLE hThread2=CreateThread(NULL,0,print222,NULL,0,NULL);
	
	WaitForSingleObject(hThread1,INFINITE);
	WaitForSingleObject(hThread2,INFINITE);
	
	CloseHandle(hThread1);
	CloseHandle(hThread2);
	CloseHandle(hMutex);//互斥锁也是一个句柄,最后记得关掉 
} 

运行结果:

cpp 复制代码
111
1111
11111
222
2222
22222

这里有个很有趣的现象,如果多运行几次,结果会出现不同:

cpp 复制代码
第一次:
111
1111
11111
222
2222
22222
第二次:
111
1111
11111
222
2222
22222
第三次:
222
2222
22222
111
1111
11111
第四次:
111
1111
11111
222
2222
22222

为什么有时线程2会先运行?说明线程2先获得了互斥锁,关键原因是主程序继续运行

cpp 复制代码
WaitForSingleObject(hThread1,INFINITE);
...(这里接着创建了hThread2)

++Windows调度器决定了哪个线程先运行(线程启动需要时间,可能只有几毫秒),在我这里,线程2先被调度运行(线程2在系统几毫秒甚至几微秒的调度时间里,抢先线程1获得了执行机会,体现了多线程调度的随机性)++

如果去掉互斥锁会怎么样:

cpp 复制代码
#include<windows.h>
#include<stdio.h>

DWORD WINAPI print111(LPVOID lpParam){
	printf("111\n");
	Sleep(100);
	printf("1111\n");
	Sleep(200);
	printf("11111\n");
	
	return 0;
}

DWORD WINAPI print222(LPVOID lpParam){
	printf("222\n");
	Sleep(100);
	printf("2222\n");
	Sleep(200);
	printf("22222\n");
	
	return 0;
}


int main(){
	
	HANDLE hThread1=CreateThread(NULL,0,print111,NULL,0,NULL);
	HANDLE hThread2=CreateThread(NULL,0,print222,NULL,0,NULL);
	
	WaitForSingleObject(hThread1,INFINITE);
	WaitForSingleObject(hThread2,INFINITE);
	
	CloseHandle(hThread1);
	CloseHandle(hThread2);
} 

运行结果:

cpp 复制代码
222
111
2222
1111
11111
22222

执行结果已经乱了,这就体现了互斥锁的重要性

四.其他技巧

1.设置线程优先级

cpp 复制代码
// Windows线程优先级(从低到高)
THREAD_PRIORITY_IDLE          // 空闲优先级(最低)
THREAD_PRIORITY_LOWEST        // 最低
THREAD_PRIORITY_BELOW_NORMAL  // 低于正常
THREAD_PRIORITY_NORMAL        // 正常(默认)
THREAD_PRIORITY_ABOVE_NORMAL  // 高于正常
THREAD_PRIORITY_HIGHEST       // 最高
THREAD_PRIORITY_TIME_CRITICAL // 时间关键(最高,几乎实时)

实例:

cpp 复制代码
#include<windows.h>
#include<stdio.h> 

HANDLE hMutex;
int counter1=0,counter2=0;

DWORD WINAPI lowPriority(LPVOID lpParam){
	while(1){
		WaitForSingleObject(hMutex,INFINITE);
		counter1++;//统计执行次数 
		ReleaseMutex(hMutex);
		//Sleep(1);//稍微让出cpu(Sleep的时间约大,优先级高的线程与优先级低的线程差距越小,因为Sleep强制让出了cpu,给了另一个线程机会) 
	}
}
DWORD WINAPI highPriority(LPVOID lpParam){
	while(1){
		WaitForSingleObject(hMutex,INFINITE);
		counter2++;//统计执行次数 
		ReleaseMutex(hMutex);
		//Sleep(1);
	
	}
}

int main(){
	
	hMutex = CreateMutex(NULL,FALSE,NULL);
	
	HANDLE hLow=CreateThread(NULL,0,lowPriority,NULL,0,NULL);
	HANDLE hHigh=CreateThread(NULL,0,highPriority,NULL,0,NULL);
	
	SetThreadPriority(hLow,THREAD_PRIORITY_LOWEST);//最低优先级
	SetThreadPriority(hHigh,THREAD_PRIORITY_HIGHEST);//最高优先级

	Sleep(1000);
	
	printf("low counter:%d\n",counter1);
	printf("high counter:%d",counter2);
	
	CloseHandle(hLow);
	CloseHandle(hHigh);
	CloseHandle(hMutex); 
	
	return 0;
}

这里会有误差,有时候低优先级线程统计数比高优先级线程统计数还高,大家可以自行测试一下

2.强制终止线程

cpp 复制代码
#include<stdio.h>
#include<windows.h>

DWORD WINAPI thread(LPVOID lpParam){
	printf("111");
	Sleep(1000);
	printf("222");
	Sleep(1000);
	printf("333");
	
	return 0;
}

int main(){
	HANDLE hThread = CreateThread(NULL,0,thread,NULL,0,NULL);
	Sleep(100);
	TerminateThread(hThread,0);//强制终止线程(危险,如果线程里的互斥锁没释放,可能导致其它线程死锁) 
	
	return 0;
} 

不想写了,睡觉去

相关推荐
前进的李工4 小时前
Agent 学习笔记 05:系统提示词应该怎么写
笔记·学习·prompt·agent·个人开发·few-shot·ai应用开发
正在走向自律5 小时前
【3/5】人工智能训练师 三级/高级工 考证学习方向:从“独立执行”到“设计、归因与全链条负责”
人工智能·学习·人工智能训练师·注方案设计·数据质控负责人·模型评测与调优辅助·ai训练项目交付
揽星河..6 小时前
【C语言】底层程序设计
c语言·开发语言
CCIE-Yasuo7 小时前
网络安全-学习数据库-Mysql-2
数据库·学习·mysql
La Pulga7 小时前
【FreeRTOS源码学习笔记】任务切换
单片机·mcu·学习·嵌入式·freertos·实时操作系统
li星野8 小时前
【学习记录】逻辑分析仪完全入门:从品牌方案到采样率、存储深度的核心概念解析
学习
旖旎夜光8 小时前
【C++ 基础入门】类和对象(上):封装、对象模型与 this 指针
开发语言·c++·笔记·学习
揽星河..9 小时前
【C语言】结构体
c语言·开发语言
WEI_YANG_JU9 小时前
初中数学教学中合作学习的研究与实践
学习
码农小韩10 小时前
FPGA学习(一)——LED点灯与开发流程
学习·fpga开发·入门·环境配置·仿真·点灯