C++–多线程
并发:多个任务同时进行(一个程序同时执行多个独立的任务),可提高性能
进程:可执行程序运行起来,操作系统分配资源的基本单位。
线程:每个进程都有个唯一主线程,主线程执行main函数,主线程生命周期和进程一致
线程可以理解为一条代码的执行道路。
多线程(并发):单个进程创建多个线程,一个进程所有线程共享地址空间(共享内存,开销更少,需考虑数据一致性问题)【每个线程都需要独立的堆栈空间(1M),线程之间切换需要保存中间状态】
多进程(并发):服务器进程之间的通信【同一服务器:管道、文件、消息队列、共享内存;
不同服务器:socket通信】
线程创建 std::thread
1.线程的开始和结束
// 主函数从main()开始执行,创建线程要从一个函数开始运行,函数运行结束
// 一般情况下主线程执行完毕,子线程还没有执行完毕,这些子线程会被操作系统强行终止
// 需要保持子线程运行状态的话,要让主线程一直保持运行
// 也存在例外情况,detach主线程不和子线程汇合,主线程先走一步了
#include <iostream>
#include <thread>
using namespace std;
// 创建线程从函数开始运行
void myPrint()
{
cout << "我的线程开始执行了..." << endl;
cout << "我的线程执行结束了..." << endl;
}
int main()
{
// 创建了线程,线程执行起点myPrint,线程开始执行
thread mytobj(myPrint); // myPrint可调用对象
// 阻塞主线程,让主线程等待子线程执行完毕,然后子线程和主线程汇合
mytobj.join();
cout << "Hello cpp!" << endl;
return 0;
}
detach
// 一旦detach,与这个主线程关联的thread对象就失去关联,子线程驻留后台运行
// 相当于被C++运行时库接管,当子线程执行完成,由运行时库负责清理线程相关资源(守护线程)
thread mytobj(myPrint);
mytobj.detach();
// joinable判断是否可以成功使用join或detach
if (mytobj.joinable())
{
cout << "join可用" << endl;
}
// 2.其他线程创建方式:类
class CTest
{
public:
CTest(int& i) : m_i(i)
{
cout << "CTest构造函数被执行了" << endl;
}
CTest(const CTest& ct) : m_i(ct.m_i)
{
cout << "CTest拷贝构造函数被执行了" << endl;
}
~CTest()
{
cout << "~CTest析构函数被执行了" << endl;
}
void operator()()
{
cout << "m_i的值为:" << m_i << endl;
}
private:
int &m_i;
};
int myint = 10;
CTest ct(myint);
thread mytobj2(ct);
mytobj2.join();
// mytobj2.detach();
// 容易产生不可预料的结果,传引用myint被主线程释放
// ct对象是被拷贝构造到了线程中,主线程ct对象销毁后,不存在问题,会析构两次
// 3.其他线程创建方式:lambda表达式
auto myLambda = [] {
cout << "线程3开始执行了" << endl;
};
thread mytobj3(myLambda);
mytobj3.join();
线程参数
// 1.传递临时对象作为线程参数
// 陷阱1:传指针有问题
// 陷阱2:构造函数有问题
// 建议(detech):int简单类型使用值传递;如果传递对象,避免隐式转换,直接构建临时对象
void myPrint(const int& i, char* pbuf)
{
cout << i << endl;
// i并不是mvar的引用,实际为值传递,那么即使主线程deteah,子线程仍然安全(不建议引用)
cout << pbuf << endl;
// pbuf和mybuf指向同一地址,使用deteah绝对有问题
}
int main()
{
int mvar = 1; // shift+f9快速监视
int& mvary = mvar;
char mybuf[] = "this is a test";
thread mytobj(myPrint, mvary, mybuf);
mytobj.join();
// mytobj.detach();
cout << "Hello, Cpp" << endl;
return 0;
}
// 防止mybuf指向同一地址,detach出现问题,使用string
// 直接转换成string,就可以保证稳定(deteah)
// 【存在mybuf被回收了,系统才把mybuf由char[]去转string问题】
void myPrint2(const int i, const string& pbuf)
{
cout << i << endl;
cout << pbuf << endl;
}
int main()
{
int mvar = 1; // shift+f9快速监视
int& mvary = mvar;
char mybuf[] = "this is a test";
thread mytobj(myPrint2, mvary, string(mybuf));
// 直接转换成string,就可以保证稳定(deteah)
mytobj.join();
// mytobj.detach();
cout << "Hello, Cpp" << endl;
return 0;
}
// 类测试
class A
{
public:
mutable int m_i;
A(int i) : m_i(i){
cout << "A的构造函数执行了" << this << " thread_id = " << this_thread::get_id() << endl; }
A(const A& a) : m_i(a.m_i) {
cout << "A的拷贝构造函数执行了" << this << " thread_id = " << this_thread::get_id() << endl; }
~A() {
cout << "A的析构函数执行了" << this << " thread_id = " << this_thread::get_id() << endl; }
void threadWork(int num)
{
cout << "子线程threadWork执行了" << this << " thread_id = " << this_thread::get_id() << endl;
}
void operator()(int num)
{
cout << "子线程operator()执行了" << this << " thread_id = " << this_thread::get_id() << endl;
}
};
void myPrint3(const int i, const A& pbuf)
{
cout << &pbuf << endl; // 打印地址
}
int main()
{
int mvar = 1;
int mvar2 = 12;
//thread mytobj(myPrint3, mvar, mvar2);
// 希望mvar2构造成A类型对象(隐式转换)
// 很可能主线程结束了,构造函数还没来得及构造
thread mytobj(myPrint3, mvar, A(mvar2));
// 构造、拷贝、析构都会执行,一定在主线程结束前构造完(安全)
//流程:构造->拷贝构造->析构->析构
cout << "主线程id是:" << this_thread::get_id() << endl;
mytobj.join();
// mytobj.detach();
cout << "Hello, Cpp" << endl;
return 0;
}
// 2.传递类对象、智能指针作为线程参数
void myPrint4(const A& pbuf)
{
pbuf.m_i = 100;
cout << "子线程myPrint4参数地址是" << &pbuf << " thread_id = " << this_thread::get_id() << endl; // 打印地址
}
void myPrint5(A& pbuf)
{
pbuf.m_i = 100;
cout << "子线程myPrint5参数地址是" << &pbuf