0.多线程介绍
为什么用多线程?
1.进程之间切换代价比较高,线程之间切换代价比较小。
2.解决CPU和IO速度不匹配问题,多线程更适合在IO切换频繁的场景
3.充分利用多核CPU资源、提高程序的并发效率
整体架构图
什么是进程,什么是线程?
进程就是运行中的程序。定义:计算机正在执行的程序实例,操作系统资源分配的基本单位。
线程是进程中的进程。定义:进程内部的执行单位。
1.创建线程
#include <thread>
std::thread t(function_name,args...);
function_name
是线程入口点的函数或可调用对象
args
是传递给函数的参数
创建线程后,可以使用t.join()
等待完成,或者使用t.detach()
分离线程,让他在后台运行。
#include <iostream>
#include <thread>
#include <string>
using namespace std;
void printHelloWorld(string msg) {
cout << msg << endl;
return;
}
int main() {
//1.创建线程
thread t1(printHelloWorld,"Jack");
//主程序等待线程执行完毕,使用join
//t1.join();
//分离线程,子程序在后台运行,配合多进程的使用
//t1.detach();
//joinable()会返回一个bool值,来判断线程是否可以调用jion和detach
bool isJoin = t1.joinable();
if (isJoin) {
t1.join();
}
return 0;
}
join()
是阻塞的,入口点的函数没有执行完毕,整个程序会卡在那个位置。
2.线程函数中的数据未定义错误
1)传递临时变量的问题
错误代码如下:
#include<iostream>
#include<thread>
using namespace std;
void add(int& x) {
x += 1;
}
int main() {
thread t(add, 1); //传递临时变量
t.join();
return 0;
}
这个会出现问题,函数要的是引用类型的变量,我们传递的是临时变量,临时变量在使用完成后会立即释放掉内存,不占内存的,那么再去拿引用就拿不到了。
解决方法:使用std::ref
传递引用类型
int a = 1; //将变量复制到一个持久的对象中
thread t(add, ref(a)); //将变量的引用传递给线程
2)传递指针或引用指向局部变量的问题
1.引用指向局部变量
错误代码:
#include<iostream>
#include<thread>
using namespace std;
thread t;
void add(int& x) {
x += 1;
}
void test() {
int a = 1;
t = thread(add, ref(a));
}
int main() {
test();
t.join();
return 0;
}
a
为局部变量,只在test中有效,那么在执行join时,test执行结束后,a变量的地址已经释放。
解决方法:把a
变成全局变量
2.传递指针
错误代码:
#include<iostream>
#include<thread>
using namespace std;
void foo(int* ptr) {
cout << *ptr << endl; //访问已经被销毁的指针
}
int main() {
int x = 1;
thread t(foo, &x); //传递临时变量
t.join();
return 0;
}
问题是:在线程函数执行时,指向局部变量x
的指针已经被销毁,从而导致未定义行动。
解决方法:将指针或者引用指向堆上的变量,或使用shared_ptr
等智能指针来管理对象的生命周期
void foo(int* ptr) {
cout << *ptr << endl;
delete ptr; // 在使用完指针后,需要手动释放内存
}
int main() {
int* ptr = new int(1); // 在堆上分配一个整数变量
thread t(foo, ptr); // 将指针传递给线程
t.join();
return 0;
}
3.互斥量解决多线程数据共享问题
在多个线程中共享数据时,需要注意线程安全问题。
如果多个线程同时访问同一个变量,并且其中至少一个线程对该变量进行了写操作,那么就会出现数据竞争问题。数据竞争可能会导致程序崩溃、产生未定义的结果,或者得到错误的结果。
为了避免数据竞争问题,需要使用同步机制来确保多个线程之间对共享数据的访问是安全的。
常见的同步机制包括互斥量、条件变量、原子操作等
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
int a = 0;
mutex mtx;
void func() {
for (int i = 0;i < 1000;i++) {
//lock()加锁操作
mtx.lock();
//在写操作之前加锁,在写操作之后解锁
a += 1;
//解锁
mtx.unlock();
}
}
int main