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

不想写了,睡觉去

相关推荐
YM52e7 分钟前
男孩子在外自我保护指南——用鸿蒙 ArkTS 构建交互式安全教育应用
学习·安全·华为·harmonyos·鸿蒙·鸿蒙系统
aXin_ya1 小时前
Ai Vibecoding学习(各个AI的讲解)
学习
fanged1 小时前
Linux内核学习16--I2C子系统(TODO)
学习
caimouse1 小时前
Reactos 第1章 概述
c语言·开发语言·架构
星间都市山脉1 小时前
Android STS(Security Test Suite)完整介绍与测试流程
android·java·linux·windows·ubuntu·android studio·androidx
.千余1 小时前
【C++】C++继承入门(下):友元、静态成员与菱形继承的底层逻辑
开发语言·c++·笔记·学习·其他
啊森要自信2 小时前
【GUI自动化测试】控件、鼠标键盘操作与多场景自动化
c语言·开发语言·python·adb·ipython
YJlio2 小时前
《Sysinternals实战指南》16.5 Ctrl2Cap 工具详解:把 Caps Lock 变成 Ctrl 的键盘改造与回退方法
linux·运维·服务器·网络·python·学习·计算机外设
xiaoliuliu123453 小时前
Sketchpad 5.0.6 几何画板安装版配置教程 Windows版:部署+桌面快捷方式创建指南
windows
菜鸟‍3 小时前
【论文学习】Segment Anything 分割一切
深度学习·学习·计算机视觉