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

不想写了,睡觉去

相关推荐
minglie16 小时前
zynq中linux系统的自动登录和免密登录
学习
茯苓gao6 小时前
嵌入式开发笔记:Qt信号槽机制深度解析——从原理到实战的全方位指南
开发语言·笔记·嵌入式硬件·qt·学习
xiaobobo33307 小时前
C语言中宏定义宏名和宏值之间的关系
c语言·宏定义·语义型
我想我不够好。7 小时前
监控学习 7.23 1.5hour
学习
FreeBuf_8 小时前
Windows NT OS内核提权漏洞PoC已公开
windows
雲帝9 小时前
Windows虚拟机UDP大包分片排障
windows·网络协议·udp
泰勒朗斯9 小时前
Helicopter FBI-405尾电机版本设置
学习
a44931536210 小时前
MacBook USB-C不充电故障诊断:从PD握手到主板供电链路
c语言·开发语言·macos·电脑
寒水馨10 小时前
Windows下载、安装scrcpy-v4.1(附安装包scrcpy-win64-v4.1.zip)
windows·scrcpy·投屏工具·安卓投屏·usb调试·android投屏·屏幕镜像
minglie111 小时前
香橙派zero3配个iic屏显示ip
linux·学习