前言
多线程的学习,说实话我也不知道有什么用,其实我能用到的无非就是一个概念thread
函数,然后再结合算法进行应用。而这种简单的应用成本几乎只需要一个小时了解一下概念就可以,根本不需要大费周章去完完整整的看视频进行学习。或许后续的编程学习会让我更加的深入?总之还是待观望。
参考博客
基本概念
并发
定义 :并发是指两个或更多的任务(独立的活动)在同一时间段内交替进行。具体来说,就是一个程序可以同时执行多个独立的任务。
单核 CPU 并发 :在过去,计算机使用单核 CPU(中央处理器),在某一时刻只能执行一个任务。操作系统通过调度程序实现任务切换(上下文切换),使得看起来像是多个任务同时进行,但实际上每个任务在短时间内交替执行。任务切换时需要保存变量状态和执行进度,这会产生时间开销。
多核 CPU 并发 :随着硬件的发展,出现了多处理器计算机,特别是在服务器和高性能计算领域。台式机中的多核 CPU(一个 CPU 内有多个运算核心)可以实现真正的并行处理,对操作系统来说,每个核心被视为独立的 CPU。这样,多个任务可以在不同的核心上同时执行,实现真正的并发。
并发的目的 :使用并发的主要目的是同时处理多个任务,以提高系统性能和效率。
可执行的程序
定义 :可执行程序是一个可以被计算机执行的文件。
进程
线程
主线程 :每个进程(即运行中的可执行程序)都有一个唯一的主线程。当执行可执行程序时,产生一个进程,同时主线程随之启动。主线程负责执行 main
函数中的代码。
线程的作用 :线程是用来执行代码的。可以把线程理解为一条代码执行的通路。
创建新线程 :除了主线程外,还可以通过编程创建其他线程。每个新线程都可以执行不同的代码路径,这意味着在同一时刻可以处理多个不同的任务。
线程数量的限制 :线程数量并不是越多越好。每个线程需要独立的堆栈空间(大约 1MB),线程之间的切换需要保存很多中间状态,这些切换操作会耗费系统资源。因此,创建过多的线程会影响程序的性能 。
创建多线程
头文件:#include <thread>
使用函数对象创建线程
thread
创建了线程mytobj
,线程执行起点(入口)是myPrint
。
join
意为汇合,子线程和主线程汇合 。join()
一般用于阻塞主线程,使得主线程需要等待子线程执行完毕才结束;
detach()
函数会将与主线程关联的thread
对象失去与主线程的关联,该子线程会留驻在后台运行。脱缰野马
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <iostream> #include <thread> using namespace std;void myprint () { cout << "This is my thread." << endl; for (int i = 0 ; i < 5 ; i++) { cout << "线程中输出 i^2 " << i * i << endl; } cout << "My thread is finished" << endl; } int main () { thread mytobj (myprint) ; mytobj.join (); cout << "BeiJing welcomes me?" << endl; for (int i = 0 ; i < 5 ; i++) { cout << "主线程中输出 -i^2 " << -i * i << endl; } return 0 ; }
mytobj.join();
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 This is my thread. 线程中输出 i^2 0 线程中输出 i^2 1 线程中输出 i^2 4 线程中输出 i^2 9 线程中输出 i^2 16 My thread is finished BeiJing welcomes me? 主线程中输出 -i^2 0 主线程中输出 -i^2 -1 主线程中输出 -i^2 -4 主线程中输出 -i^2 -9 主线程中输出 -i^2 -16
mytobj.detach();
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 BeiJing welcomes me? 主线程中输出 -i^2 0 主线程中输出 -i^2 -1 主线程中输出 -i^2 -4 主线程中输出 -i^2 -9 主线程中输出 -i^2 This is my thread. 线程中输出 i^2 0 线程中输出 i^2 1 线程中输出 i^2 4 线程中输出 i^2 9 线程中输出 i^2 16 My thread is finished -16
注:运行多次结果可能都不相同
注:调用detach()
后不能再用join()
,否则系统报错。
joinable()
判断是否可以成功使用join()
或者detach()
如果返回true
,证明可以调用join()
或者detach()
如果返回false
,证明调用过join()
或者detach()
,join()
和detach()
都不能再调用了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #include <iostream> #include <thread> using namespace std;void myprint () { cout << "This is my thread." << endl; cout << "My thread is finished" << endl; } int main () { thread mytobj (myprint) ; if (mytobj.joinable ()) { cout << "可以调用可以调用join()或者detach()" << endl; } else { cout << "不能调用可以调用join()或者detach()" << endl; } mytobj.detach (); if (mytobj.joinable ()) { cout << "可以调用可以调用join()或者detach()" << endl; } else { cout << "不能调用可以调用join()或者detach()" << endl; } cout << "BeiJing welcomes me?" << endl; return 0 ; }
结果:
1 2 3 4 5 可以调用可以调用join()或者detach() This is my thread. 不能调用可以调用join()或者detach() BeiJing welcomes me? My thread is finished
使用类对象创建线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include <iostream> #include <thread> using namespace std;class Ta { public : void operator () () { cout << "This is my thread." << endl; for (int i = 0 ; i < 5 ; i++) { cout << "线程中输出 i^2 " << i * i << endl; } cout << "My thread is finished" << endl; } }; int main () { Ta ta; thread myThread (ta) ; myThread.join (); cout << "BeiJing welcomes me?" << endl; for (int i = 0 ; i < 5 ; i++) { cout << "主线程中输出 -i^2 " << -i * i << endl; } return 0 ; }
mytobj.join();
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 This is my thread. 线程中输出 i^2 0 线程中输出 i^2 1 线程中输出 i^2 4 线程中输出 i^2 9 线程中输出 i^2 16 My thread is finished BeiJing welcomes me? 主线程中输出 -i^2 0 主线程中输出 -i^2 -1 主线程中输出 -i^2 -4 主线程中输出 -i^2 -9 主线程中输出 -i^2 -16
mytobj.detach();
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 BeiJing welcomes me?This is my thread. 线程中输出 i*mi 0 线程中输出 i*mi 6 线程中输出 i*mi 12 线程中输出 i*mi 18 线程中输出 i*mi 24 My thread is finished 主线程中输出 -i^2 0 主线程中输出 -i^2 -1 主线程中输出 -i^2 -4 主线程中输出 -i^2 -9 主线程中输出 -i^2 -16
注:主线程运行结束之后,main()函数中的变量会被销毁,但对象会被复制到线程中去,复制的对象依旧存在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 #include <iostream> #include <thread> using namespace std;class Ta {public : int & mi; Ta (int &i):mi (i) { cout << "Ta()构造函数被执行" << endl; } Ta (const Ta& ta) :mi (ta.mi) { cout << "Ta()拷贝构造函数被执行" << endl; } ~Ta () { cout << "~Ta()析构函数被执行" << endl; } void operator () () { cout << "This is my thread." << endl; for (int i = 0 ; i < 5 ; i++) { cout << "线程中输出 i*mi " << i * mi << endl; } cout << "My thread is finished" << endl; } }; int main () { int i = 6 ; Ta ta (i) ; thread myThread (ta) ; myThread.join (); cout << "BeiJing welcomes me?" << endl; for (int i = 0 ; i < 5 ; i++) { cout << "主线程中输出 -i^2 " << -i * i << endl; } return 0 ; }
myThread.join()
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Ta()构造函数被执行 Ta()拷贝构造函数被执行 Ta()拷贝构造函数被执行 ~Ta()析构函数被执行 This is my thread. 线程中输出 i*mi 0 线程中输出 i*mi 6 线程中输出 i*mi 12 线程中输出 i*mi 18 线程中输出 i*mi 24 My thread is finished ~Ta()析构函数被执行 BeiJing welcomes me? 主线程中输出 -i^2 0 主线程中输出 -i^2 -1 主线程中输出 -i^2 -4 主线程中输出 -i^2 -9 主线程中输出 -i^2 -16 ~Ta()析构函数被执行
myThread.detach()
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Ta()构造函数被执行 Ta()拷贝构造函数被执行 Ta()拷贝构造函数被执行 ~Ta()析构函数被执行 BeiJing welcomes me? 主线程中输出 -i^2 0 主线程中输出 -i^2 -1 主线程中输出 -i^2 -4 主线程中输出 -i^2 -9 This is my thread.主线程中输出 -i^2 -16 线程中输出 i*mi 0 线程中输出 i*mi 6 线程中输出 i*mi 12 线程中输出 i*mi 18 线程中输出 i*mi 24 My thread is finished ~Ta()析构函数被执行 ~Ta()析构函数被执行
lamda表达式创建线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <iostream> #include <thread> using namespace std;int main () { auto lambdaThread = [] { cout << "This is my thread." << endl; for (int i = 0 ; i < 5 ; i++) { cout << "线程中输出 i*i " << i * i << endl; } cout << "My thread is finished" << endl; }; thread myThread (lambdaThread) ; myThread.join (); cout << "BeiJing welcomes me?" << endl; for (int i = 0 ; i < 5 ; i++) { cout << "主线程中输出 -i^2 " << -i * i << endl; } return 0 ; }
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 This is my thread. 线程中输出 i*i 0 线程中输出 i*i 1 线程中输出 i*i 4 线程中输出 i*i 9 线程中输出 i*i 16 My thread is finished BeiJing welcomes me? 主线程中输出 -i^2 0 主线程中输出 -i^2 -1 主线程中输出 -i^2 -4 主线程中输出 -i^2 -9 主线程中输出 -i^2 -16
传递临时对象
值传递与指针传递
当使用 detach() 分离线程时,传递参数应注意以下问题:
传递 int 等简单类型参数时,建议使用值传递 ,而不使用引用传递,避免参数回收的问题(主线程首先结束,引用传递的参数被回收了,导致子线程的参数无效 );
当传递复杂类型参数时(如类等),应避免发生隐式类型转换(发生隐式类型转换时,可能会发生主线程结束了,要传递的参数还没进行隐式类型转换,而此时参数已经被回收了),可通过生成临时对象的方式(例如下面代码中的 std::string(s) 和 class(value)都在主线程中首先生成临时对象,再进行参数传递)来进行参数传递,同时在函数参数内用引用(例如 myprint 函数中 const myclass &cls)进行接收;
非必要情况,应尽量避免使用 detach() 来分离线程 ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include <iostream> #include <thread> using namespace std;void myPrint (const int & i, char * pmybuf) { cout << "i的地址为:" << &i << endl; cout << "pmybuf的地址为:" << &pmybuf << endl; } int main () { int mvar = 1 ; int & mvary = mvar; char mybuf[] = "this is a test" ; cout << "mvar的地址为:" << &mvar << endl; cout << "mvary的地址为:" << &mvary << endl; cout << "mybuf的地址为:" << &mybuf << endl; thread myThread (myPrint, mvar, mybuf) ; myThread.join (); cout << "BeiJing welcomes me?" << endl; }
结果:
1 2 3 4 5 6 mvar的地址为:0x16b4b6914 mvary的地址为:0x16b4b6914 pmybuf的地址为:0x16b4b6918 i的地址为:0x12df040d0 mybuf的地址为:0x16b53ef10 BeiJing welcomes me?
注:i
并不是mvar
的引用,实际上是值传递 。
但是指针传递时,在detach()
的子线程中,会有main()函数运行完时被销毁的可能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <iostream> #include <thread> #include <string> using namespace std;void myPrint (const int i, const string& pmybuf) { cout << i << endl; cout << "i的地址为:" << &i << endl; cout << pmybuf << endl; cout << "pmybuf的地址为:" << &pmybuf << endl; } int main () { int mvar = 1 ; int & mvary = mvar; char mybuf[] = "this is a test" ; cout << "mvar的地址为:" << &mvar << endl; cout << "mvary的地址为:" << &mvary << endl; cout << "mybuf的地址为:" << &mybuf << endl; thread myThread (myPrint, mvar, mybuf) ; myThread.join (); cout << "BeiJing welcomes me?" << endl; }
1 2 3 4 5 6 7 8 mvar的地址为:0x16f3f2914 mvary的地址为:0x16f3f2914 mybuf的地址为:0x16f3f2918 1 i的地址为:0x16f47aedc this is a test pmybuf的地址为:0x16f47af20 BeiJing welcomes me?
传递临时类对象
创建线程同时构造临时对象传递参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include <iostream> #include <thread> #include <string> using namespace std;class myclass { public : int m_i; myclass (int a): m_i (a) { cout << "构造函数myclass::myclass(int a)执行" << this << endl ;} ; myclass (const myclass &a): m_i (a.m_i) { cout << "拷贝构造函数myclass::myclass(const int a)执行" << this << endl ;} ; ~myclass () { cout << "析构函数myclass::~myclass()执行" << this << endl ;}; }; void myPrint (const int i, const myclass& pmybuf) { cout << i << endl; cout << "i的地址为:" << &i << endl; cout << "pmybuf的地址为:" << &pmybuf << endl; } int main () { int mvar = 1 ; int myvar2 = 66 ; thread myThread (myPrint, mvar, myclass(myvar2)) ; myThread.detach (); cout << "BeiJing welcomes me?" << endl; }
1 2 3 4 5 6 7 8 构造函数myclass::myclass(int a)执行0x16d3ba91c 拷贝构造函数myclass::myclass(const int a)执行0x158605f14 析构函数myclass::~myclass()执行0x16d3ba91c BeiJing welcomes me? 1 i的地址为:0x16d442f1c pmybuf的地址为:0x158605f14 析构函数myclass::~myclass()执行0x158605f14
不使用临时对象传递
线程ID std::this_thread::get_id()
在子线程中构造对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <iostream> #include <thread> #include <string> using namespace std;class myclass { public : int m_i; myclass (int a): m_i (a) { cout << "构造函数myclass::myclass(int a)执行" << this << " threadid = " << std::this_thread::get_id () << endl ;} ; myclass (const myclass &a): m_i (a.m_i) { cout << "拷贝构造函数myclass::myclass(const int a)执行" << this << " threadid = " << std::this_thread::get_id () << endl ;} ; ~myclass () { cout << "析构函数myclass::~myclass()执行" << this << " threadid = " << std::this_thread::get_id () << endl ;}; }; void myPrint (const myclass& pmybuf) { cout << "pmybuf的地址为:" << &pmybuf << " 子线程id为 " << std::this_thread::get_id () << endl; } int main () { cout<< "主线程id为 " << std::this_thread::get_id () << endl; int myvar = 66 ; thread myThread (myPrint, myvar) ; myThread.join (); cout << "BeiJing welcomes me?" << endl; }
结果:
1 2 3 4 5 主线程id为 0x1e8b9fac0 构造函数myclass::myclass(int a)执行0x16d196f3c threadid = 0x16d197000 pmybuf的地址为:0x16d196f3c 子线程id为 0x16d197000 析构函数myclass::~myclass()执行0x16d196f3c threadid = 0x16d197000 BeiJing welcomes me?
子线程需要使用主线程的变量 value 来进行构造和析构操作(具体可见构造和析构发生的线程 id 与主线程 id 不同)
当使用 detach 分离两个线程时,可能会出现主线程执行完毕了,主线程的变量 value 已被回收,而子线程来不及拷贝 value 变量来进行类的构造和析构操作;
使用临时对象传递
在主线程中构造对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <iostream> #include <thread> #include <string> using namespace std;class myclass { public : int m_i; myclass (int a): m_i (a) { cout << "构造函数myclass::myclass(int a)执行" << this << " threadid = " << std::this_thread::get_id () << endl ;} ; myclass (const myclass &a): m_i (a.m_i) { cout << "拷贝构造函数myclass::myclass(const int a)执行" << this << " threadid = " << std::this_thread::get_id () << endl ;} ; ~myclass () { cout << "析构函数myclass::~myclass()执行" << this << " threadid = " << std::this_thread::get_id () << endl ;}; }; void myPrint (const myclass& pmybuf) { cout << "pmybuf的地址为:" << &pmybuf << " 子线程id为 " << std::this_thread::get_id () << endl; } int main () { cout<< "主线程id为 " << std::this_thread::get_id () << endl; int myvar = 66 ; thread myThread (myPrint, myclass(myvar)) ; myThread.join (); cout << "BeiJing welcomes me?" << endl; }
结果:
1 2 3 4 5 6 7 主线程id为 0x1e8b9fac0 构造函数myclass::myclass(int a)执行0x16b872914 threadid = 0x1e8b9fac0 拷贝构造函数myclass::myclass(const int a)执行0x127e05f10 threadid = 0x1e8b9fac0 析构函数myclass::~myclass()执行0x16b872914 threadid = 0x1e8b9fac0 pmybuf的地址为:0x127e05f10 子线程id为 0x16b8fb000 析构函数myclass::~myclass()执行0x127e05f10 threadid = 0x16b8fb000 BeiJing welcomes me?
临时对象的构造和析构发生在主线程中,即使说主线程的变量value被回收,子线程已经拷贝了value的变量,不会因为主线程的结束而出问题
修改临时对象内参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include <iostream> #include <thread> #include <string> using namespace std;class myclass { public : mutable int m_i; myclass (int a): m_i (a) { cout << "构造函数myclass::myclass(int a)执行" << this << " threadid = " << std::this_thread::get_id () << endl ;} ; myclass (const myclass &a): m_i (a.m_i) { cout << "拷贝构造函数myclass::myclass(const int a)执行" << this << " threadid = " << std::this_thread::get_id () << endl ;} ; ~myclass () { cout << "析构函数myclass::~myclass()执行" << this << " threadid = " << std::this_thread::get_id () << endl ;}; }; void myPrint (const myclass& pmybuf) { pmybuf.m_i = 2333 ; cout << "pmybuf的地址为:" << &pmybuf << " pmybuf的值为:" << pmybuf.m_i << " 子线程id为 " << std::this_thread::get_id () << endl; } int main () { cout<< "主线程id为 " << std::this_thread::get_id () << endl; myclass A (666 ) ; cout<< "类A的地址" << &A << " 类A的值" << A.m_i << endl; thread myThread (myPrint, A) ; myThread.join (); cout<< "类A的值" << A.m_i << endl; cout << "BeiJing welcomes me?" << endl; }
结果:
1 2 3 4 5 6 7 8 主线程id为 0x1e8b9fac0 构造函数myclass::myclass(int a)执行0x16cf16924 threadid = 0x1e8b9fac0 类A的地址0x16cf16924 类A的值666 拷贝构造函数myclass::myclass(const int a)执行0x144e05f10 threadid = 0x1e8b9fac0 pmybuf的地址为:0x144e05f10 pmybuf的值为:2333 子线程id为 0x16cf9f000 析构函数myclass::~myclass()执行0x144e05f10 threadid = 0x16cf9f000 类A的值666 BeiJing welcomes me?
并没有修改main函数中类的值,只修改了拷贝的类中的值
std::ref
函数能将地址传输
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include <iostream> #include <thread> #include <string> using namespace std;class myclass { public : int m_i; myclass (int a): m_i (a) { cout << "构造函数myclass::myclass(int a)执行" << this << " threadid = " << std::this_thread::get_id () << endl ;} ; myclass (const myclass &a): m_i (a.m_i) { cout << "拷贝构造函数myclass::myclass(const int a)执行" << this << " threadid = " << std::this_thread::get_id () << endl ;} ; ~myclass () { cout << "析构函数myclass::~myclass()执行" << this << " threadid = " << std::this_thread::get_id () << endl ;}; }; void myPrint (myclass& pmybuf) { pmybuf.m_i = 2333 ; cout << "pmybuf的地址为:" << &pmybuf << " pmybuf的值为:" << pmybuf.m_i << " 子线程id为 " << std::this_thread::get_id () << endl; } int main () { cout<< "主线程id为 " << std::this_thread::get_id () << endl; myclass A (666 ) ; cout<< "类A的地址" << &A << " 类A的值" << A.m_i << endl; thread myThread (myPrint, std::ref(A)) ; myThread.join (); cout<< "类A的值" << A.m_i << endl; cout << "BeiJing welcomes me?" << endl; }
结果:
1 2 3 4 5 6 7 主线程id为 0x1e8b9fac0 构造函数myclass::myclass(int a)执行0x16dbfa924 threadid = 0x1e8b9fac0 类A的地址0x16dbfa924 类A的值666 pmybuf的地址为:0x16dbfa924 pmybuf的值为:2333 子线程id为 0x16dc83000 类A的值2333 BeiJing welcomes me? 析构函数myclass::~myclass()执行0x16dbfa924 threadid = 0x1e8b9fac0
修改了类中的值
智能指针传递临时对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <iostream> #include <thread> #include <memory> using namespace std;void myPrint (unique_ptr<int > ptn) { cout<< "子线程中的指针地址为" << ptn << endl; cout << "thread_id = " << std::this_thread::get_id () << endl; } int main () { unique_ptr<int > up (new int (10 )) ; cout<< "主线程中的指针地址为" << up << endl; cout << "thread_id = " << std::this_thread::get_id () << endl; thread myThread (myPrint, std::move(up)) ; myThread.join (); return 0 ; }
1 2 3 4 主线程中的指针地址为0x142e05fa0 thread_id = 0x1e8b9fac0 子线程中的指针地址为0x142e05fa0 thread_id = 0x16b70f000
成员函数指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <thread> #include <iostream> using namespace std;class myclass { public : int m_i; myclass (int a): m_i (a) { cout << "构造函数myclass::myclass(int a)执行" << this << " threadid = " << std::this_thread::get_id () << endl ;} ; myclass (const myclass &a): m_i (a.m_i) { cout << "拷贝构造函数myclass::myclass(const int a)执行" << this << " threadid = " << std::this_thread::get_id () << endl ;} ; ~myclass () { cout << "析构函数myclass::~myclass()执行" << this << " threadid = " << std::this_thread::get_id () << endl ;}; void thread_work (int num) { cout << "[子线程 thread_work 执行]" << this << " threadid = " << std::this_thread::get_id () << endl ; } }; int main () { myclass myobj (10 ) ; thread mytobj (&myclass::thread_work, myobj, 15 ) ; mytobj.join (); return 0 ; }
thread mytobj(&myclass::thread_work, myobj, 15);
结果:
1 2 3 4 5 构造函数myclass::myclass(int a)执行0x16ba22928 threadid = 0x1e8b9fac0 拷贝构造函数myclass::myclass(const int a)执行0x15b8040d8 threadid = 0x1e8b9fac0 [子线程 thread_work 执行]0x15b8040d8 threadid = 0x16baab000 析构函数myclass::~myclass()执行0x15b8040d8 threadid = 0x16baab000 析构函数myclass::~myclass()执行0x16ba22928 threadid = 0x1e8b9fac0
std::thread mytobj(&A::thread_work, std::ref(myobj), 15);
结果:
1 2 3 构造函数myclass::myclass(int a)执行0x16fc2a928 threadid = 0x1e8b9fac0 [子线程 thread_work 执行]0x16fc2a928 threadid = 0x16fcb3000 析构函数myclass::~myclass()执行0x16fc2a928 threadid = 0x1e8b9fac0
发现std::ref
没有调用拷贝构造函数
调用运算符重载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include <thread> #include <iostream> using namespace std;class myclass { public : int m_i; myclass (int a): m_i (a) { cout << "构造函数myclass::myclass(int a)执行" << this << " threadid = " << std::this_thread::get_id () << endl ;} ; myclass (const myclass &a): m_i (a.m_i) { cout << "拷贝构造函数myclass::myclass(const int a)执行" << this << " threadid = " << std::this_thread::get_id () << endl ;} ; ~myclass () { cout << "析构函数myclass::~myclass()执行" << this << " threadid = " << std::this_thread::get_id () << endl ;}; void thread_work (int num) { cout << "[子线程 thread_work 执行]" << this << " threadid = " << std::this_thread::get_id () << endl ; } void operator () (int num) { cout << "[子线程()执行]" << this << " threadid = " << std::this_thread::get_id () << endl ; } }; int main () { myclass myobj (10 ) ; thread mytobj (myobj, 15 ) ; mytobj.join (); return 0 ; }
thread mytobj(myobj, 15);
结果:
1 2 3 4 5 构造函数myclass::myclass(int a)执行0x16b03e928 threadid = 0x1e8b9fac0 拷贝构造函数myclass::myclass(const int a)执行0x14fe05e38 threadid = 0x1e8b9fac0 [子线程()执行]0x14fe05e38 threadid = 0x16b0c7000 析构函数myclass::~myclass()执行0x14fe05e38 threadid = 0x16b0c7000 析构函数myclass::~myclass()执行0x16b03e928 threadid = 0x1e8b9fac0
thread mytobj(std::ref(myobj), 15);
结果:
1 2 3 构造函数myclass::myclass(int a)执行0x16d85e928 threadid = 0x1e8b9fac0 [子线程()执行]0x16d85e928 threadid = 0x16d8e7000 析构函数myclass::~myclass()执行0x16d85e928 threadid = 0x1e8b9fac0
创建多个线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <thread> #include <iostream> #include <vector> using namespace std;void myprint (int inum) { cout << "myprint线程开始执行了,线程编号 = " << inum << endl; cout << "thread_id = " << std::this_thread::get_id () << endl ; cout << "myprint线程执行结束了,线程编号 = " << inum << endl; } int main () { vector<thread> mythreads; for (int i = 0 ; i < 5 ; i++) { mythreads.push_back (thread (myprint, i)); } for (auto iter = mythreads.begin (); iter != mythreads.end (); ++iter) { iter->join (); } cout << "BeiJing welcomes me?" << endl; return 0 ; }
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 myprint线程开始执行了,线程编号 = myprint线程开始执行了,线程编号 = 02 thread_id = 0x16d193000 myprint线程执行结束了,线程编号 = 0 thread_id = 0x16d2ab000 myprint线程执行结束了,线程编号 = myprint线程开始执行了,线程编号 = 21 myprint线程开始执行了,线程编号 = 3 thread_id = myprint线程开始执行了,线程编号 = 0x16d337000 4thread_id = myprint线程执行结束了,线程编号 = 30x16d21f000 myprint线程执行结束了,线程编号 = 1thread_id = 0x16d3c3000 myprint线程执行结束了,线程编号 = 4 BeiJing welcomes me?
数据共享问题
只读数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <thread> #include <iostream> #include <vector> using namespace std;vector<int > e_v = { 1 ,2 ,3 }; void myprint (int inum) { cout << "id 为" << std::this_thread::get_id () << "的线程 打印 g_v 的值" << e_v[0 ] << e_v[1 ] << e_v[2 ] << endl; } int main () { vector<thread> mythreads; for (int i = 0 ; i < 5 ; i++) { mythreads.push_back (thread (myprint, i)); } for (auto iter = mythreads.begin (); iter != mythreads.end (); ++iter) { iter->join (); } cout << "BeiJing welcomes me?" << endl; return 0 ; }
1 2 3 4 5 6 id 为id 为0x16f50b000的线程 打印 g_v 的值id 为0x16f623000的线程 打印 g_v 的值112233 id 为0x16f73b000的线程 打印 g_v 的值123 id 为0x16f6af000的线程 打印 g_v 的值123 0x16f597000的线程 打印 g_v 的值123 BeiJing welcomes me?
独占互斥量
实际例子:网络游戏服务器有两个自己创建的线程一个线程:收集玩家发来的命令(简化问题:用一个数字代表),将命令数据写入一个队列一个线程:从队列中取出玩家发送来的命令,解析,执行玩家要干的动作
lock(),unlock()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 #include <thread> #include <mutex> #include <iostream> #include <list> using namespace std;class A { public : void inMsgRecvQueue () { for (int i = 0 ; i < 100 ; ++i) { cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl; my_mutex.lock (); msgRecvQueue.push_back (i); my_mutex.unlock (); } } bool outMsgLULProc (int &command) { my_mutex.lock (); if (!msgRecvQueue.empty ()) { int command = msgRecvQueue.front (); msgRecvQueue.pop_front (); my_mutex.unlock (); return true ; } my_mutex.unlock (); return false ; } void outMsgRecvQueue () { int command = 0 ; for (int i = 0 ; i < 100 ; ++i) { bool result = outMsgLULProc (command); if (result == true ) { cout << "outMsgRecvQueue()执行,释放一个元素" << i << endl; } else { cout << "outMsgRecvQueue()执行,队列为空!" << endl; } } } private : list<int > msgRecvQueue; std::mutex my_mutex; }; int main () { A myobja; thread myOutMsgobj (&A::outMsgRecvQueue, &myobja) ; thread myInMsgobj (&A::inMsgRecvQueue, &myobja) ; myOutMsgobj.join (); myInMsgobj.join (); return 0 ; }
std::lock_guard类模板
std::lock_guard类模板:直接取代lock()和unlock();也就是说,使用了lock_guard()后就不能再使用lock()和unlock()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void inMsgRecvQueue () { for (int i = 0 ; i < 100 ; ++i) { cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl; td::lock_guard<std::mutex> sbguard (my_mutex) ; msgRecvQueue.push_back (i); } } bool outMsgLULProc (int &command) { std::lock_guard<std::mutex> sbguard (my_mutex) ; if (!msgRecvQueue.empty ()) { int command = msgRecvQueue.front (); msgRecvQueue.pop_front (); return true ; } return false ; }
死锁
c++中的死锁(至少有两把锁):比如有两把锁,锁一,锁二,两个线程,线程A,线程B;(业务:需要把两把锁都锁上)
此时此刻,死锁就发生了
线程A锁不了锁二,流程走不下去,所以锁一解不开;
线程B锁不了锁一,流程走不下去,所以锁二解不开;
错误示范
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 #include <thread> #include <mutex> #include <iostream> #include <list> using namespace std;class A { public : void inMsgRecvQueue () { for (int i = 0 ; i < 10000 ; ++i) { cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl; my_mutex1.lock (); my_mutex2.lock (); msgRecvQueue.push_back (i); my_mutex2.unlock (); my_mutex1.unlock (); } } bool outMsgLULProc (int & command) { my_mutex2.lock (); my_mutex1.lock (); if (!msgRecvQueue.empty ()) { int command = msgRecvQueue.front (); msgRecvQueue.pop_front (); my_mutex1.unlock (); my_mutex2.unlock (); return true ; } my_mutex1.unlock (); my_mutex2.unlock (); return false ; } void outMsgRecvQueue () { int command = 0 ; for (int i = 0 ; i < 10000 ; ++i) { bool result = outMsgLULProc (command); if (result == true ) { cout << "outMsgRecvQueue()执行,释放一个元素" << i << endl; } else { cout << "outMsgRecvQueue()执行,队列为空!" << endl; } } } private : list<int > msgRecvQueue; std::mutex my_mutex1; std::mutex my_mutex2; }; int main () { A myobja; thread myOutMsgobj (&A::outMsgRecvQueue, &myobja) ; thread myInMsgobj (&A::inMsgRecvQueue, &myobja) ; myOutMsgobj.join (); myInMsgobj.join (); return 0 ; }
互斥量的lock()顺序一致
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 void inMsgRecvQueue () { for (int i = 0 ; i < 10000 ; ++i) { cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl; my_mutex1.lock (); my_mutex2.lock (); msgRecvQueue.push_back (i); my_mutex2.unlock (); my_mutex1.unlock (); } } bool outMsgLULProc (int & command) { my_mutex1.lock (); my_mutex2.lock (); if (!msgRecvQueue.empty ()) { int command = msgRecvQueue.front (); msgRecvQueue.pop_front (); my_mutex2.unlock (); my_mutex1.unlock (); return true ; } my_mutex2.unlock (); my_mutex1.unlock (); return false ; }
std::lock()函数模板
std::lock()
用于处理多个互斥量;一次锁住两个或者两个以上的互斥量(至少两个);(同时锁住多个互斥量的情况比较少见);
不存在在多线程中,因为锁的顺序导致死锁的风险问题;
std::lock():如果互斥量中有一个每锁住,他就会释放自己锁住的,然后就等在那里,等所有互斥量都锁住,才往下走;
要么两个互斥量都锁住或者两个互斥量都释放;如果只锁一个,另外一个没成功,则立即释放锁住的;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 void inMsgRecvQueue () { for (int i = 0 ; i < 10000 ; ++i) { cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl; std::lock (my_mutex1, my_mutex2); msgRecvQueue.push_back (i); my_mutex2.unlock (); my_mutex1.unlock (); } } bool outMsgLULProc (int & command) { std::lock (my_mutex1, my_mutex2); if (!msgRecvQueue.empty ()) { int command = msgRecvQueue.front (); msgRecvQueue.pop_front (); my_mutex2.unlock (); my_mutex1.unlock (); return true ; } my_mutex2.unlock (); my_mutex1.unlock (); return false ; }
std::lock_guard的std::adopt_lock参数
std::adopt_lock
是一个结构体对象,起一个标记作用,表示互斥量已经进行lock了,不需要在构造函数里面在对其进行lock;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 void inMsgRecvQueue () { for (int i = 0 ; i < 10000 ; ++i) { cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl; std::lock (my_mutex1, my_mutex2); std::lock_guard<std::mutex> sbguard1 (my_mutex1, std::adopt_lock) ; std::lock_guard<std::mutex> sbguard2 (my_mutex2, std::adopt_lock) ; msgRecvQueue.push_back (i); } } bool outMsgLULProc (int & command) { std::lock (my_mutex1, my_mutex2); std::lock_guard<std::mutex> sbguard1 (my_mutex1, std::adopt_lock) ; std::lock_guard<std::mutex> sbguard2 (my_mutex2, std::adopt_lock) ; if (!msgRecvQueue.empty ()) { int command = msgRecvQueue.front (); msgRecvQueue.pop_front (); return true ; } return false ; }
unique_lock()类模板
unique_lock比lock_guard灵活很多(多出来很多用法),效率差一点
取代lock_guard()
1 2 3 4 5 6 7 8 9 10 void inMsgRecvQueue () { for (int i = 0 ; i < 100 ; ++i) { cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl; std::unique_lock<std::mutex> myUniLock (my_mutex) ; msgRecvQueue.push_back (i); } }
参数
std::adopt_lock
1 2 3 4 5 6 7 8 9 10 void inMsgRecvQueue () { for (int i = 0 ; i < 100 ; ++i) { cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl; my_mutex.lock (); std::unique_lock<std::mutex> myUniLock (my_mutex, std::adopt_lock) ; msgRecvQueue.push_back (i); } }
std::try_to_lock
尝试用mutex的lock()去锁定这个mutex,但如果没有锁定成功,会立即返回,不会阻塞在那里;使用try_to_lock的原因是防止其他的线程锁定mutex太长时间,导致本线程一直阻塞在lock这个地方前提:不能提前lock();
owns_lock()方法判断是否拿到锁,如拿到返回true
1 2 3 4 5 6 7 8 9 10 11 12 13 14 for (int i = 0 ; i < 100 ; ++i){ cout << "inMsgRecvQueue()执行" << endl; std::unique_lock<std::mutex> myUniLock (my_mutex, std::try_to_lock) ; if (sbguard.owns_lock () == true ) { cout<<"拿到了锁,执行命令,插入一个元素" << i << endl; msgRecvQueue.push_back (i); } else { cout<<"inMsgRecvQueue()执行但没有拿到锁,执行其他命令" <<endl; } }
std::defer_lock:
成员函数
lock():加锁
1 2 unique_lock<mutex> myUniLock (myMutex, defer_lock) ;myUniLock.lock ();
不用自己unlock();
unlock():解锁
1 2 3 4 5 6 7 unique_lock<mutex> myUniLock (myMutex, defer_lock) ;myUniLock.lock (); myUniLock.unlock (); myUniLock.lock ();
因为一些非共享代码要处理,可以暂时先unlock(),用其他线程把它们处理了,处理完后再lock()
try_lock()
与std::try_to_lock
用法类似
release():
unique_lock<mutex> myUniLock(my_mutex);
相当于把myMutex和myUniLock绑定在了一起,release()就是解除绑定,返回它所管理的mutex对象的指针,并释放所有权
所有权由ptx接管,如果原来mutex对象处理加锁状态,就需要ptx在以后进行解锁了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void inMsgRecvQueue () { for (int i = 0 ; i < 100 ; ++i) { cout << "inMsgRecvQueue()执行" << endl; unique_lock<mutex> myUniLock (my_mutex) ; cout<<"my_mutex的地址为" << &my_mutex << endl; mutex* ptx = myUniLock.release (); cout<<"ptx的地址为" << ptx << endl; msgRecvQueue.push_back (i); ptx->unlock (); } }
lock的代码段越少,执行越快,整个程序的运行效率越高。
锁住的代码少,叫做粒度细,执行效率高;
锁住的代码多,叫做粒度粗,执行效率低;
所有权的传递
unique_lock<mutex> myUniLock(myMutex);
把myMutex和myUniLock绑定在了一起,也就是myUniLock拥有myMutex的所有权
使用move转移
myUniLock1拥有myMutex的所有权,myUniLock1可以把自己对myMutex的所有权转移,但是不能复制。
1 2 3 4 5 6 7 8 9 10 11 12 13 void inMsgRecvQueue () { for (int i = 0 ; i < 100 ; ++i) { cout << "inMsgRecvQueue()执行" << endl; unique_lock<mutex> myUniLock1 (my_mutex) ; cout<<"myUniLock1的地址为" << &myUniLock1 << endl; unique_lock<mutex> myUniLock2 (std::move(myUniLock1)) ; cout<<"myUniLock2的地址为" << &myUniLock2 << endl; msgRecvQueue.push_back (i); } }
构造一个返回值函数转移
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 unique_lock<mutex> return_unique_lock () { unique_lock<mutex> myUniLock (my_mutex) ; cout<<"函数中myUniLock的地址为" << &myUniLock << endl; return myUniLock; } void inMsgRecvQueue () { for (int i = 0 ; i < 100 ; ++i) { cout << "inMsgRecvQueue()执行" << endl; unique_lock<mutex> myUniLock = return_unique_lock (); cout<<"myUniLock的地址为" << &myUniLock << endl; msgRecvQueue.push_back (i); } }
条件变量condition_variable
wait(), notify_one()
wait()
用于等待,当lambda表达式返回true时,wait()
直接返回;当lambda表达式返回false时,wait()
将解锁互斥量,并在本行堵塞,堵塞直到某个线程调用notify_one()
成员函数为止
如果wait()没有第二个参数,则与lambda表达式返回false的效果一样,即在本行堵塞,直到某个线程调用notify_one()成员函数
当其他线程用notify_one()
堵塞的wait()
唤醒后,wait()线程将不断尝试获取锁 (注:不一定能狗立刻获取到锁)
假如wait()
所在线程正在执行其余代码段,并不处于wait()
的状态,此时notify_one()
无效果(相当于宣告一直没锁)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 #include <iostream> #include <thread> #include <mutex> #include <list> #include <condition_variable> using namespace std;class A { public : void inMsgRecvQueue () { for (int i = 0 ; i < 100 ; i++) { cout << "Running inMsgRecvQueue(), insert one elem" << i << endl; unique_lock<mutex> guard1 (mutex1) ; msgRecvQueue.push_back (i); cond.notify_one (); } return ; } void outMsgRecvQueue () { int command = 0 ; while (true ) { std::unique_lock<std::mutex> guard1 (mutex1) ; cond.wait (guard1, [this ]{ if (!msgRecvQueue.empty ()) return true ; else return false ; }); command = msgRecvQueue.front (); msgRecvQueue.pop_front (); guard1.unlock (); cout << "Running outMsgRecvQueue(), get one elem: " << command << endl; if (command == 999 ) break ; } } private : mutex mutex1; list<int > msgRecvQueue; condition_variable cond; }; int main () { A a; std::thread OutMsg (&A::outMsgRecvQueue, &a) ; std::thread InMsg (&A::inMsgRecvQueue, &a) ; OutMsg.join (); InMsg.join (); return 0 ; }
notify_all()
notify_all()
可以同时唤醒多个线程,,当多个线程都会等待被唤醒时,所有线程都会被唤醒;(被唤醒的多个线程可能会竞争同一个互斥锁(假设线程共用一个锁))
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 #include <iostream> #include <thread> #include <mutex> #include <list> #include <condition_variable> using namespace std;class A { public : void inMsgRecvQueue () { for (int i = 0 ; i < 100 ; i++) { cout << "Running inMsgRecvQueue(), insert one elem" << i << endl; unique_lock<mutex> guard1 (mutex1) ; msgRecvQueue.push_back (i); cond.notify_all (); } return ; } void outMsgRecvQueue () { int command = 0 ; while (true ) { std::unique_lock<std::mutex> guard1 (mutex1) ; cond.wait (guard1, [this ]{ if (!msgRecvQueue.empty ()) return true ; else return false ; }); command = msgRecvQueue.front (); msgRecvQueue.pop_front (); cout << "Running outMsgRecvQueue(), get one elem: " << command << " 线程id" << this_thread::get_id () << endl; guard1.unlock (); if (command == 999 ) break ; } } private : mutex mutex1; list<int > msgRecvQueue; condition_variable cond; }; int main () { A a; std::thread OutMsg1 (&A::outMsgRecvQueue, &a) ; std::thread OutMsg2 (&A::outMsgRecvQueue, &a) ; std::thread InMsg (&A::inMsgRecvQueue, &a) ; OutMsg1.join (); OutMsg2.join (); InMsg.join (); return 0 ; }
超时锁
std::timed_mutex
带有超时功能的独占互斥量
try_lock_for()
try_lock_for()
等待一段时间来尝试获取锁,若无法获取锁,则线程继续执行(跳过临界区的内容)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 void inMsgRecvQueue () { for (int i = 0 ; i < 100 ; i++) { std::chrono::seconds timeout (100 ) ; if (my_mutex.try_lock_for (timeout)) { std::cout << "inMsgRecvQueue()执行,插入一个元素:" << i << std::endl; msgRecvqueue.push_back (i); my_mutex.unlock (); } else { std::chrono::seconds sleeptime (100 ); std::this_thread::sleep_for (sleeptime); std::cout << "can not get the lock" << std::endl; } } }
try_lock_until()
try_lock_until()
是直到某一时间内都在尝试获取锁,如果超过了规定的时间点仍无法获取锁,线程就会继续执行(跳过临界区的内容)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 void inMsgRecvQueue () { for (int i = 0 ; i < 100 ; i++) { std::chrono::seconds timeout (100 ) ; if (my_mutex.try_lock_until (std::chrono::steady_clock::now () + timeout) { std::cout << "inMsgRecvQueue()执行,插入一个元素:" << i << std::endl; msgRecvqueue.push_back (i); my_mutex.unlock (); } else { std::chrono::seconds sleeptime (100 ); std::this_thread::sleep_for (sleeptime); std::cout << "can not get the lock" << std::endl; } } }
递归独占互斥量
std::recursive_mutex
创建递归的独占互斥量,允许一个线程对其多次加锁lock()
和解锁unlock()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #include <iostream> #include <thread> #include <mutex> using namespace std;std::recursive_mutex mtx; void functionB () { std::lock_guard<std::recursive_mutex> lock (mtx) ; cout << "functionB() called by thread " << std::this_thread::get_id () << endl; } void functionA () { std::lock_guard<std::recursive_mutex> lock (mtx) ; cout << "functionA() called by thread " << std::this_thread::get_id () << endl; functionB (); } void threadFunc () { functionA (); } int main () { std::thread t1 (threadFunc) ; std::thread t2 (threadFunc) ; t1.join (); t2.join (); return 0 ; }
单例设计模式共享数据
单例设计模型
单例设计模式要求某一个类最多创建一个对象,这个对象即单例对象(全局唯一实例对象)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 #include <iostream> using namespace std;class MyCAS { public : static MyCAS *GetInstance () { if (m_instance == NULL ){ m_instance = new MyCAS (); static CGar c1; } return m_instance; } void func () { std::cout << "test sample!" << std::endl; } class CGar { public : ~CGar (){ if (MyCAS::GetInstance) { cout << "调用子类的析构函数,释放m_instance" << endl; delete MyCAS::m_instance; MyCAS::m_instance = NULL ; } } }; private : MyCAS (){}; static MyCAS *m_instance; }; MyCAS* MyCAS::m_instance = NULL ; int main () { MyCAS *sample1 = MyCAS::GetInstance (); MyCAS *sample2 = MyCAS::GetInstance (); cout << sample1 << " " << sample2 << endl; sample1->func (); return 0 ; }
1 2 3 0x15be05e70 0x15be05e70 test sample! 调用子类的析构函数,释放m_instance
数据竞争
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 #include <iostream> #include <thread> #include <mutex> using namespace std; class MyCAS {public : static MyCAS *GetInstance () { if (m_instance == NULL ) { m_instance = new MyCAS (); static CGar c1; } return m_instance; } void func () { std::cout << "test sample!" << std::endl; } class CGar { public : ~CGar (){ if (MyCAS::GetInstance){ delete MyCAS::m_instance; MyCAS::m_instance = NULL ; } } }; private : MyCAS (){}; static MyCAS *m_instance; }; MyCAS* MyCAS::m_instance = NULL ; void mythread () { cout << "start thread" << std::endl; MyCAS *p_a = MyCAS::GetInstance (); p_a->func (); cout << "thread end" << std::endl; return ; } int main () { std::thread thread1 (mythread) ; std::thread thread2 (mythread) ; thread1.join (); thread2.join (); return 0 ; }
问题:可能两个线程会同时进入if(m_instance == NULL)
的判断语句,导致多个线程同时创建类对象的情况
解决方法:加入互斥量,保证只有一个if(m_instance == NULL)
会执行
1 2 3 4 5 6 7 8 9 10 static MyCAS *GetInstance () { std::unique_lock<std::mutex> guard1 (mutex1) ; if (m_instance == NULL ) { m_instance = new MyCAS (); static CGar c1; } return m_instance; }
问题:线程每一次创建时都需要调用std::unique_lock<std::mutex> guard1(mutex1);
,效率低下
双重检验锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 static MyCAS *GetInstance () { if (m_instance == NULL ) { std::unique_lock<std::mutex> guard1 (mutex1) ; if (m_instance == NULL ) { m_instance = new MyCAS (); static CGar c1; } } return m_instance; }
call_once()
std::call_once()
的功能是确保函数 func()
只会被调用一次;(即 std::call_once()
具有互斥量的能力,相对于互斥量其消耗的资源更少)
std::call_once()
需要和一个标记进行结合使用,这个标记决定对象的函数 func()
是否被调用;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 #include <iostream> #include <thread> #include <mutex> std::once_flag g_flag; class MyCAS {private : static void CreateInstance () { m_instance = new MyCAS (); static CGar c1; } public : static MyCAS *GetInstance () { std::call_once (g_flag, CreateInstance); return m_instance; } void func () { std::cout << "test sample!" << std::endl; } class CGar { public : ~CGar (){ if (MyCAS::GetInstance){ delete MyCAS::m_instance; MyCAS::m_instance = NULL ; } } }; private : MyCAS (){}; static MyCAS *m_instance; }; MyCAS* MyCAS::m_instance = NULL ; void mythread () { std::cout << "start thread" << std::endl; MyCAS *p_a = MyCAS::GetInstance (); p_a->func (); std::cout << "thread end" << std::endl; return ; } int main () { std::thread thread1 (mythread) ; std::thread thread2 (mythread) ; thread1.join (); thread2.join (); return 0 ; }
原子操作
原子操作std::atomic
是一种在多线程环境下不可分割的操作,确保操作不会被其他线程中断。这对于保证数据一致性和避免数据竞争非常重要。原子操作通常适用于对单个变量 的操作,而不是一段代码或多个变量(使用互斥量)。
高效性 :原子操作通常比使用互斥量(std::mutex
)进行加锁和解锁更高效,因为原子操作在硬件层面提供了支持。
线程安全 :原子操作保证了在多线程环境下操作的完整性,避免了数据竞争问题。
简洁性 :使用原子操作可以简化代码,因为不需要显式地进行锁定和解锁操作。
原子操作的适用场景包括但不限于以下几种:
计数器 :如全局计数器、访问计数器等。
标志变量 :如状态标志、完成标志等。
指针操作 :如原子指针的读取和写入。
简洁的条件检查和交换操作 :如比较并交换(compare-and-swap)。
计数器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <iostream> #include <thread> #include <atomic> #include <vector> std::atomic<int > counter (0 ) ;void incrementCounter (int n) { for (int i = 0 ; i < n; ++i) { counter++; } } int main () { std::vector<std::thread> threads; for (int i = 0 ; i < 10 ; ++i) { threads.push_back (std::thread (incrementCounter, 1000 )); } for (auto & t : threads) { t.join (); } std::cout << "Final counter value: " << counter << std::endl; return 0 ; }
标志变量
原子操作可以用于设置和检查标志变量,例如在多线程环境下通知某些状态变化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #include <iostream> #include <thread> #include <atomic> using namespace std;std::atomic<bool > ready (false ) ;void waitForReady () { std::chrono::seconds dura (1 ) ; while (!ready) { cout << "thread runs 1s" << endl; std::this_thread::sleep_for (dura); } std::cout << "Thread " << std::this_thread::get_id () << " proceeds." << std::endl; } int main () { std::thread t1 (waitForReady) ; std::thread t2 (waitForReady) ; std::this_thread::sleep_for (std::chrono::seconds (5 )); ready = true ; t1.join (); t2.join (); return 0 ; }
指针操作
.store()
以原子方式写入内容,.load()
以原子方式读对象的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <iostream> #include <thread> #include <atomic> using namespace std;std::atomic<int *> atomic_ptr (nullptr ) ;void setPointer (int * ptr) { atomic_ptr.store (ptr); } void usePointer () { while (!atomic_ptr.load ()) { std::this_thread::yield (); } cout << "Pointer value: " << *atomic_ptr.load () << endl; } int main () { int value = 42 ; std::thread t1 (usePointer) ; std::thread t2 (setPointer, &value) ; t1.join (); t2.join (); return 0 ; }
比较并交换
expected :这是一个左值引用,表示预期的旧值。如果原子变量的当前值与 expected
相等,则将其更新为 desired
,否则将原子变量的当前值写回 expected
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <iostream> #include <thread> #include <atomic> using namespace std;std::atomic<int > atomic_val (0 ) ;void compareAndSwap (int oldVal, int newVal) { int expected = oldVal; if (atomic_val.compare_exchange_strong (expected, newVal)) { cout << "Thread " << std::this_thread::get_id () << " updated the value to " << newVal << endl; } else { cout << "Thread " << std::this_thread::get_id () << " failed to update the value. Current value is " << atomic_val.load () << endl; } } int main () { std::thread t1 (compareAndSwap, 0 , 10 ) ; std::thread t2 (compareAndSwap, 0 , 20 ) ; t1.join (); t2.join (); cout << "Final value: " << atomic_val.load () << endl; return 0 ; }
future成员函数
std::async
std::async
创建一个异步任务 ,其不一定会创建一个新线程去执行该任务;
传递普通函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <iostream> #include <thread> #include <mutex> #include <future> using namespace std;int mythread () { cout << "thread start, id: " << std::this_thread::get_id () << endl; int value = 5000 ; std::chrono::microseconds dura (value) ; std::this_thread::sleep_for (dura); cout << "thread end, id: " << this_thread::get_id () << endl; return 5 ; } int main () { cout << "main thread id: " << std::this_thread::get_id () << endl; std::future<int > result = std::async (mythread); cout << "resule.get(): " << result.get () << endl; cout << "main thread continue ..." << endl; return 0 ; }
1 2 3 4 5 main thread id: 0x1fc76fac0 resule.get(): thread start, id: 0x16b143000 thread end, id: 0x16b143000 5 main thread continue ...
传递类成员函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #include <iostream> #include <thread> #include <mutex> #include <future> using namespace std;class A { public : int mythread (int mypar) { cout << mypar << endl; cout << "thread start, id: " << std::this_thread::get_id () << endl; int value = 5000 ; std::chrono::microseconds dura (value) ; std::this_thread::sleep_for (dura); cout << "thread end, id: " << this_thread::get_id () << endl; return 5 ; } }; int main () { A a; int temp = 666 ; cout << "main thread id: " << std::this_thread::get_id () << endl; std::future<int > result = std::async (&A::mythread, &a, temp); cout << "resule.get(): " << result.get () << endl; cout << "main thread continue ..." << endl; return 0 ; }
1 2 3 4 5 6 main thread id: 0x1fc76fac0 resule.get(): 666 thread start, id: 0x16bc5f000 thread end, id: 0x16bc5f000 5 main thread continue ...
延迟调用
std::launch::deferred
表示调用线程入口函数将会被延迟到std::future
的wait()
或get()
调用,当wait()
或者get()
没有被调用时,线程入口函数不会被调用(线程不会被创建)
1 std::future<int > result = std::async (std::launch::deferred, &A::mythread, &a, temp);
1 2 3 4 5 6 main thread id: 0x1fc76fac0 resule.get(): 666 thread start, id: 0x1fc76fac0 thread end, id: 0x1fc76fac0 5 main thread continue ...
并没有创建新线程,是在主线程中调用的线程入口函数
参数std::launch::async
,操作系统会强制创建一个新线程 来执行异步任务(同步运行)
默认参数是std::launch::async | std::launch::deferred
,操作系统会同步运行或者延迟调用
std::future_status
std::future_status
包含以下三个值:
std::future_status::ready
:表示异步操作已经完成,结果已经准备好,可以调用 future.get()
来获取结果。
std::future_status::timeout
:表示在指定的等待时间内,异步操作还没有完成。
std::future_status::deferred
:表示异步操作被延迟执行,仅在调用 future.get()
或 future.wait()
时才会执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 #include <iostream> #include <thread> #include <mutex> #include <future> using namespace std;class Sample { public : int thread (int value) { cout << "thread id: " << std::this_thread::get_id () << endl; std::chrono::seconds dura (value) ; std::this_thread::sleep_for (dura); return 5 ; } }; int main () { Sample sample; int value = 3 ; cout << "main thread id: " << std::this_thread::get_id () << endl; std::future<int > result = std::async (&Sample::thread, &sample, value); std::future_status status = result.wait_for (std::chrono::seconds (1 )); if (status == std::future_status::timeout) { cout << "thread timeout!" << endl; } else if (status == std::future_status::ready) { cout << "result.get(): " << result.get () << endl; } else if (status == std::future_status::deferred) { cout << "deferred, result.get(): " << result.get () << endl; } std::cout << "main thread continue ..." << std::endl; return 0 ; }
Thread与saync区别
如果使用std::thread
创建的线程太多导致系统资源紧张,可能创建失败,系统报告异常
如果使用std::async
创建的任务过多导致系统资源紧张,不会报告异常不会崩溃。如果系统因为资源紧张无法创建新线程的时候,std::async
不会创建新线程,而是后续调用result.get()
请求结果,异步任务就运行在result.get()
语句所在的线程上。
判断异步任务的执行状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include <iostream> #include <thread> #include <future> using namespace std;int threadFunc () { cout << "Thread function id: " << std::this_thread::get_id () << endl; return -1 ; } int main () { cout << "Main thread id: " << std::this_thread::get_id () << endl; std::future<int > result = std::async (std::launch::deferred | std::launch::async, threadFunc); std::future_status status = result.wait_for (std::chrono::seconds (0 )); if (status == std::future_status::deferred) { cout << "Task is deferred, running in the main thread." << endl; cout << "Result: " << result.get () << endl; } else { cout << "Task is running in a new thread." << endl; cout << "Result: " << result.get () << endl; } return 0 ; }
std::shared_future
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 #include <iostream> #include <thread> #include <future> #include <vector> #include <chrono> using namespace std;int compute (int value) { cout << "Computing in thread id: " << std::this_thread::get_id () << endl; std::this_thread::sleep_for (std::chrono::seconds (value)); return value * 2 ; } void printResult (shared_future<int > fut, int threadNum) { cout << "Thread " << threadNum << " id: " << std::this_thread::get_id () << " waiting for result..." << endl; int result = fut.get (); cout << "Thread " << threadNum << " received result: " << result << endl; } int main () { int value = 3 ; cout << "Main thread id: " << std::this_thread::get_id () << endl; std::future<int > fut = std::async (compute, value); std::shared_future<int > sharedFut = fut.share (); std::vector<std::thread> threads; for (int i = 1 ; i <= 3 ; ++i) { threads.emplace_back (printResult, sharedFut, i); } for (auto & t : threads) { t.join (); } cout << "Main thread continue ..." << endl; return 0 ; }
1 2 3 4 5 6 7 8 9 Main thread id: 0x1fc76fac0 Computing in thread id: 0x16dc07000 Thread 1 id: 0x16dc93000 waiting for result... Thread 2 id: 0x16dd1f000 waiting for result... Thread 3 id: 0x16ddab000 waiting for result... Thread Thread 12 received result: received result: 66 Thread 3 received result: 6 Main thread continue ...
std::packaged_task
std::packaged_task
用于打包任务,其包装各种可调用对象,方便后续作为线程入口函数
包装普通子函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <iostream> #include <thread> #include <mutex> #include <future> using namespace std;int mythread (int mypar) { cout << mypar << endl; cout << "thread start, id: " << std::this_thread::get_id () << endl; int value = 5000 ; std::chrono::microseconds dura (value) ; std::this_thread::sleep_for (dura); cout << "thread end, id: " << this_thread::get_id () << endl; return 5 ; } int main () { cout << "main thread id: " << std::this_thread::get_id () << endl; std::packaged_task<int (int ) > mypt (mythread) ; std::thread t1 (std::ref(mypt), 666 ) ; t1.join (); std::future<int > result = mypt.get_future (); cout << "result.get(): " << result.get () << endl; cout << "main thread continue ..." << endl; return 0 ; }
包装lamda表达式
1 2 3 4 5 6 7 8 9 std::packaged_task<int (int ) > mypt ([](int mypar){ cout << mypar << endl; cout << "thread start, id: " << std::this_thread::get_id() << endl; int value = 5000 ; std::chrono::microseconds dura(value); std::this_thread::sleep_for(dura); cout << "thread end, id: " << this_thread::get_id() << endl; return 5 ; }) ;
直接调用包装函数
1 2 3 mypt (666 );std::future<int > result = mypt.get_future (); cout << "result.get(): " << result.get () << endl;
1 2 3 4 5 6 main thread id: 0x1fc76fac0 666 thread start, id: 0x1fc76fac0 thread end, id: 0x1fc76fac0 result.get(): 5 main thread continue ...
没有创建子线程,都在主线程中执行
std::promise()
std::promise
用于在其他线程中使用某个线程中的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include <iostream> #include <thread> #include <future> using namespace std;void thread1_func (promise<int >& prom) { cout << "thread1_func thread id: " << std::this_thread::get_id () << endl; std::this_thread::sleep_for (std::chrono::milliseconds (100 )); int result = 42 ; prom.set_value (result); cout << "Thread 1: value set to " << result << endl; } void thread2_func (future<int >& fut) { cout << "thread2_func thread id: " << std::this_thread::get_id () << endl; int value = fut.get (); cout << "Thread 2: value read as " << value << endl; } int main () { cout << "main thread id: " << std::this_thread::get_id () << endl; promise<int > prom; future<int > fut = prom.get_future (); thread t1 (thread1_func, std::ref(prom)) ; thread t2 (thread2_func, std::ref(fut)) ; t1.join (); t2.join (); return 0 ; }
1 2 3 4 5 main thread id: 0x1fc76fac0 thread1_func thread id: 0x16f7ff000 thread2_func thread id: 0x16f88b000 Thread 1: value set to 42 Thread 2: value read as 42