系列文章目录
【C++学习】谈谈我对C++代码重构的认识
【C++学习】谈谈我对C++中delete的看法
前言
C++11在语言的层面上引入了线程,而在之前是通过Pthread库的方式引入线程库,现在通过标准库thread引入了对线程的支持,使得线程的创建和启动更加简单且更安全。引入线程的目的主要是为了实现并发,能够实现在同一进程中多个线程共享相同的地址空间,可以访问进程中的大部分数据,指针和引用可以在线程间进行传递。
一、创建和启动线程:
通过声明一个线程对象便可以创建并启动线程,在构造的参数列表中写出线程绑定的函数,在函数后用逗号来写入参数列表。
#include <iostream>
#include <thread>
using namespace std;
//定义一个简单的用于输出的函数
void func(int num) {
cout << num << endl;
}
int main() {
for (int i = 0; i < 5; i++) {
//每次循环创建一个线程并启动
thread th(func, i); //创建并启动线程,参数放在函数的后面
th.detach(); //detach方式使线程在后台运行,稍后介绍
}
getchar();
return 0;
}
输出:可以看到输出的结果并不是按照顺序递增的数列,而是乱序且中间有空格的一个数列,那是因为使用了detach()
让线程在后台运行,运行的顺序是不确定的,每次运行的结果可能都不一样。
023
4
1
C:\Users\Astro\source\repos\testTime\Release\testTime.exe (进程 42100)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
如果将detach()
换成join()
便会看到递增的数列:
int main() {
for (int i = 0; i < 5; i++) {
thread th(func, i);
th.join(); //换成join()的方式结束线程
}
getchar();
return 0;
}
输出:
0
1
2
3
4
C:\Users\Astro\source\repos\testTime\Release\testTime.exe (进程 56088)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
Note:
- 线程参数的传递 : 默认的会将传递的参数以拷贝的方式复制到线程空间,即使参数的类型是引用
- 如果想在调用线程的函数中更新变量,就需要使用
std::ref()
来将变量的引用传入线程,而不是一个拷贝。
class _tagNode
{
public:
int a;
int b;
};
void func(_tagNode &node)
{
node.a = 10;
node.b = 20;
}
void f()
{
_tagNode node;
thread t(func, std::ref(node));
t.join();
cout << node.a << endl ;
cout << node.b << endl ;
}
二、线程的结束:
detach
方式,启动的线程自主在后台运行,当前的代码继续往下执行,不等待新线程结束。前面代码所使用的就是这种方式。detach相当于失去了对线程的控制,当对象析构时线程会继续在后台执行。join
方式,等待启动的线程完成,才会继续往下执行。假如前面的代码使用这种方式,其输出就会0,1,2,3,因为每次都是前一个线程输出完成了才会进行下一个循环,启动下一个新线程。
三、线程与互斥锁:
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
int cnt1 = 20, cnt2 = 20;
using namespace std;
int cnt = 20;
mutex m;
void t1(){
while (cnt > 0){
//m.lock();
//使用lock_guard更加安全,它会自动解锁
lock_guard<mutex> lockGuard(m);
if (cnt > 0) {
--cnt;
cout << cnt << endl;
}
//m.unlock();
}
}
void t2(){
while (cnt > 0){
m.lock();
if (cnt > 0){
cnt = cnt - 2;
cout << cnt << endl;
}
m.unlock();
}
}
int main()
{
//这里可能会出现递减1 也可能会出现递减2的数列,因为资源只有一个cnt,当主函数运行时执行的顺序是不确定的,但是因为在每个线程中都设置了
//互斥锁,所以线程之间不会去争夺cnt这个资源,而是互斥的执行
thread th1(t1);
thread th2(t2);
th1.join();
th2.join();
return 0;
}
总结
C++中线程还有许多其他的操作,还有很多种线程之间的锁:互斥锁、条件锁、自旋锁、读写锁、递归锁等等。本文只介绍了一些基本的操作,如有不正,敬请指出!