最近无意发现了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;
}
不想写了,睡觉去