创建线程:
要创建线程,我们需要一个可调用的函数对象,作为线程的入口点,在C++11中,我们可以使用函数指针,函数对象或者lambda表达式来实现。
创建线程的语法如下:
#include<thread>
std::thread th(function_name,args...);
function_name是线程入口点的函数或可调用对象;
args...是传递给函数的参数;
【示例】:
如上图所示,程序崩溃其主要原因在于,main函数为主线程,在main中创建 th1 为子线程,创建出子线程之后,两个函数程序分开运行,会出现一种状况,那就是,主线程,也就是main函数运行结束,但子线程运行未结束,则会出现上图中的报错以及系统崩溃的情况。那么如何解决呢?
join() 函数;
join() 函数,的作用在于,它要等到子线程运行结束后,在结束,可以说是一种阻塞等待。
detach() 函数:
分离线程,子线程会和主线程分离,
如上图所示,在主线程结束后,子线程还未来得及执行输出hello world。
joinable() 函数,判断一个线程能否使用join()函数的函数,
- 互斥量解决多线程数据共享问题
数据共享问题:
在多个线程中共享数据时,需要注意线程安全问题。如果多个线程同时访问一个变量,并且至少有一个线程对该变量,进行了写操作,那么就会出现数据竞争问题。数据竞争可能会导致程序崩溃,产生未定义的结果,或者得到错误的结果。
为了避免数据竞争的问题,需要使用同步机制来确保多个线程之间对共享数据的访问是安全的。常见的同步机制包括互斥量,条件变量,原子操作等。
如上图所示,理论上来讲运行结果应该是20000,但打印在控制台上的结果却不是,这是为什么呢?
这是因为,两个线程访问同一个变量时会出现冲突,产生数据竞争所导致的结果,那么如何避免呢?
#include<mutex>,添加此头文件;
锁:mutex,
可运用锁,将线程和需要访问的变量“锁”住,就可避免竞争问题。如下图所示。
死锁:
概念:假设有两个线程T1和T2,它们需要对两个互斥量mt1和mt2进行访问,而且需要按照以下顺序获取互斥量的所有权。
T1先获取mt1的所有权,再获取mt2的所有权。
T2先获取mt2的所有权,再获取mt1的所有权。
当两个线程同时运行时,就会出现死锁问题。
因为T1获取了mt1的所有权,但无法获取mt2的所有权,
而 T2获取了mt2的所有权,但无法获取mt1的所有权。
如上图所示为死锁。
解决问题的方法很简单,只需要两个线程都先获取m1后获取m2,或者都先获取m2后获取m1。