5分钟学会简单的C++多线程编程
前言
本篇博客使用的库:<windows.h> 提供的线程api(当然是使用操作系统自己提供的线程库的最好了啊)(关于线程函数的>官方文档<)
看博客前需要一些基本条件条件:
- 对线程有基本的理解
- 简单的C++面向过程编程能力
博客主要内容:
- 创造单个简单的线程。
- 创造单个带参数的线程。
- 如何等待线程结束。
- 创造多个线程,并使用互斥量来防止资源抢占。
会使用之后,直接跳到“汇总”,复制模板来用就行
相关博客:实现一个简单的线程池
线程教程
创建一个线程:CreateThread()
- 函数声明
//返回值:一个HANDLE类型的值,表示线程的句柄,可用于等待线程等函数
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, // 不重要,一般设置为NULL
SIZE_T dwStackSize, // 堆栈大小,不重要,一般设置为0,表示使用默认堆栈大小
LPTHREAD_START_ROUTINE lpStartAddress, // 函数指针,传入函数名或指针即可
__drv_aliasesMem LPVOID lpParameter, // 参数指针,供函数使用
DWORD dwCreationFlags, // 不重要,一般设置为0
LPDWORD lpThreadId // 指针,用于保存线程的id,一般设置为NULL
);]
- 使用方法
// 声明线程函数的模板:
DWORD WINAPI threadname(LPVOID lpParamter) // 函数名字可随意
{
/* 这里填入你的代码 */
return 0L;
}
// 根据声明的函数创造一个线程
// 若函数没有参数,传入函数名字即可,其它参数参考下方示例
HANDLE hThread = CreateThread(NULL, 0, threadname, NULL, 0, NULL);
- 使用实例
记得等待线程结束!不然函数(不一定是主函数哦,而是CreatThread所处的函数)结束退出后,会释放资源,导致未结束的线程产生各种奇怪的错误!
#include<iostream>
#include<windows.h>
using namespace std;
// 编写了一个我的线程函数
DWORD WINAPI MyThread(LPVOID lpParamter)
{
cout << "fuck the multithread !\n";
return 0L;
}
int main ()
{
// 创造线程
CreateThread(NULL, 0, MyThread, NULL, 0, NULL);
// 记得等待线程结束
system("PAUSE");
return 0;
}
运行结果:
fuck the multithread !
创建一个带参线程:lpParameter参数
- 关于void指针
lpParamter 跟 void指针使用方法类似,void指针可以指向任何数据,所以我们可以把任何类形指针的值赋给void指针(但不能把void指针的值直接赋给其它类型的指针!)
下方是int 与 void 指针相互转换的例子。
int a = 0;
int *intp = &a;
// int指针 转 void指针
void * voidp = intp; // 合法!不会报错
// void指针 转 int指针
intp = voidp; // 不合法! void* 指针不能直接赋值给 int*指针,会报错!
intp = (int *)voidp// 合法!利用强制类型转换,告诉编译器,voidp指针指向的是int数据。
- 直接实例
我假设需要传入一个整形参数,在声明函数时,只需要多一行需要这样的操作:
DWORD WINAPI MyThread(LPVOID lpParamter)
{
// 把lpParamter当成void指针就完事儿了
int *a = (int *)lpParamter;
cout << "I have " << a[0] << " dolors!\n";
return 0L;
}
而创造函数时候,传入一个整形指针就行了
int main ()
{
int a = 0;
int *p = &a;
// 创造线程,注意我把p指针作为参数传入了,它也就成为了上方函数的lpParamter
CreateThread(NULL, 0, MyThread, p, 0, NULL);
// 记得等待线程结束
system("PAUSE");
return 0;
}
运行结果:
I have 0 dolors!
至于传入更多的参数,可以自己设计一个struct或者class数据结构,并传入其指针就行了,或者也可以传入数组。
至于返回值,也可以利用传入的指针来接收。
等待指定线程结束:WaitForSingleObject()
这个特别简单,此函数就两个参数,第一个参数是创建线程时可得到的返回值(HANDLE)也就是句柄,第二个参数不用关心,传入INFINITE就行了。
使用实例:
HANDLE hThread = CreateThread(NULL, 0, thread1, NULL, 0, NULL);
// 利用得到的句柄等待线程结束
WaitForSingleObject(hThread, INFINITE);
有了这个函数,再也不用Sleep或者While(1)啦。
多线程资源加锁:CreateMutex()
1.创建多个线程
动动脑子都能想到,直接利用循环或多次调用GreatThread函数不就行了么对吧
代码实例
DWORD WINAPI MyThread(LPVOID lpParamter)
{
// 把lpParamter当成void指针就完事儿了
int *a = (int *)lpParamter;
cout << "I have " << a[0] << " dolors!" << endl;
return 0L;
}
int main()
{
int a[10];
for (int i = 0; i < 10; i++)
{
a[i] = i;
HANDLE hThread = CreateThread(NULL, 0, MyThread, a + i, 0, NULL);
}
system("PAUSE");
return 0;
}
但是,直接运行程序,你会发现输出结果特别诡异!顺序完全乱掉了,换行也乱掉了。
I have I have I have 8I have 7I have 5I have 1I have 3 dolors!4 dolors!
2 dolors! dolors!
dolors!
dolors!
dolors!
I have 6I have dolors!
9 dolors!
I have 0 dolors!
动动脑子也能知道,线程几乎是同时运行的,谁想输出就输出了,管你是不是刚输出到了一半。
所以我们就加锁,使 “输出” 这个资源,只能在一个时间内被单个线程使用
- 为资源加锁
创建方法:
// 声明一个句柄
HANDLE cout_mutex;
// 创建一个锁
cout_mutex = CreateMutex(NULL, FALSE, NULL);
使用方法:
// 等待其它线程释放锁,并获取锁的使用权
WaitForSingleObject(cout_mutex, INFINITE);
// 获取锁之后,只要没有解锁,其它线程就会阻塞在WaitForSingleObject()语句。
做一些工作(使用需要互斥的资源等)
// 解锁!
ReleaseMutex(cout_mutex);
使用实例:
HANDLE cout_mutex;
DWORD WINAPI MyThread(LPVOID lpParamter)
{
// 把lpParamter当成void指针就完事儿了
int *a = (int *)lpParamter;
WaitForSingleObject(cout_mutex, INFINITE);
cout << "I have " << a[0] << " dolors!" << endl;
ReleaseMutex(cout_mutex);
return 0L;
}
int main()
{
int a[10];
cout_mutex = CreateMutex(NULL, FALSE, NULL);
for (int i = 0; i < 10; i++)
{
a[i] = i;
HANDLE hThread = CreateThread(NULL, 0, MyThread, a + i, 0, NULL);
}
system("PAUSE");
return 0;
}
运行结果:
可以看出输出正常了。
I have 0 dolors!
I have 1 dolors!
I have 7 dolors!
I have 5 dolors!
I have 4 dolors!
I have 6 dolors!
I have 3 dolors!
I have 9 dolors!
I have 2 dolors!
指定线程运行核心:SetThreadAffinityMask()
现在的cpu一般都是多核的了(任务管理器->性能 -> CPU 即可看到信息),所以有时候需指定线程到某个核心,可以更主动的对cpu资源进行分配。
- 函数声明
DWORD_PTR SetThreadAffinityMask(HANDLE hThread, DWORD_PTR dwThreadAffinityMask);
- 使用方法:
第一个参数是线程的句柄(HANDLE),第二个参数用来指定CPU核心
比如,你要指定进程到第0个CPU上,则mask=0×01
第1个CPU:mask=0×02
第2个CPU:mask=0×04 (注意不是0×03)
第3个CPU:mask=0×08
以此类推。
如果要指定多个CPU:
比如第0、1个:mask=0×03
第1、2个:mask=0×06
以此类推。
如果CPU个数不足,则会进行取模操作。比如一共4个CPU,则mask=0×0010则和0×01一样
(此段文字转自其它博客,但找不到源链接,就不贴出来了)
- 代码实例:
// 绑定到第三个核心
HANDLE hThread = CreateThread(NULL, 0, MyThread, a + i, 0, NULL);
SetThreadAffinityMask(hThread, 0x08);
汇总
线程函数
DWORD WINAPI threadname(LPVOID lpParamter) // 函数名字可随意
{
int *a = (int *)lpParamter;
return 0L;
}
创建线程
HANDLE hThread = CreateThread(NULL, 0, threadname, NULL, 0, NULL);
等待线程
WaitForSingleObject(hThread , INFINITE);
互斥量
HANDLE cout_mutex;
cout_mutex = CreateMutex(NULL, FALSE, NULL);
WaitForSingleObject(cout_mutex, INFINITE);
ReleaseMutex(cout_mutex);
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/36738.html