C11多线程
线程库
thread里面有什么函数?
上代码!(举例子)
在VS2019进行
为了支持线程库,我们需要设置,在项目的管理属性设置。
sleep函数
相当于全局函数,不依赖于线程对象。
上代码!
我们看到程序崩溃了,什么原因?
当我们程序进行执行时,进入到主函数,我们给这个进程分配了资源:代码资源,fun函数,主函数都是代码资源,还有数据区,还有我们的堆区,栈区。
在Linux2.6以后的版本,当我们进入主函数,创建一个线程叫做主线程。
我们的主函数变成主线程,主函数由两部分构成:一部分是分配给主线程的栈,一部分是主线程的信息部分。我们在主线程的栈定义一个x值,创建了一个线程对象t1,我们要创建t1线程对象,拥有和主线程一样的资源,有自己的线程信息和自己的线程栈。
程序接着向下执行到睡眠函数。睡眠的目的是使得主线程把CPU资源让步出来。
我们的t1线程开始执行,到达进程找到fun代码,执行在t1线程栈帧上。在t1线程栈帧开辟空间,定义i,a,打印a,给出睡眠200毫秒,接着打印,睡眠,当这个线程执行完毕之后,并不意味着线程对象销毁,线程对象依然存在,我们回退到主线程,主线程结束,但是t1线程对象没有结束,所以系统会报错。
所以我们要这个函数:jion();
这个函数作用不但是等待当前线程完成其执行,而且更重要的目的是当线程执行完对应的函数之后,我们把线程对象对应的资源给释放。
第一个参数是函数的地址,第二个参数是线程的参数,把x给a变量。
如果线程函数没有参数
执行没问题!
我们增加代码:再创建一个线程!
会不会打印出6,7,8,9?
答案是不可能的!!!
当我们程序执行的时候,进入到主函数,相当于进程,我们给进程分配了代码区,数据区,栈区,堆区;这个栈给给主函数,这个主函数变成主线程,有主函数的栈帧和主函数的线程对象两部分。我们创建t1线程对象,也有两部分构成:栈和t1线程信息。我们创建t2线程对象,也有两部分构成:栈和t2线程信息。
虽然是一份funa函数创建两份线程。
我们向下执行,主线程执行睡眠。线程和进程一样,有就绪,执行,睡眠,阻塞。主线程让步CPU资源。t1,t2线程运行,调动funa函数,这个函数只有一份。我们在线程t1去调动funa时候,这个函数在t1的栈空间调动,在线程t1定义x,x++;我们有可能睡眠之后,t2线程执行,调动funa函数,在t2的栈空间定义x,x++,这2个x是不一样的,在不同的栈空间中。不管怎么打,有可能是乱序,但是不可能是突破4的。
我们创建多个线程,可以共享同一份函数,但是函数的局部变量是在每个线程中独立存在,互不关系。不需要加互斥量哦!
我们接着往下看
这个有没有可能打印5,6,7,8,9?
答案是可能的!因为这是个全局量。
我们从主函数执行,我们给进程分配代码区,全局变量区,栈区,堆区。
当我们主函数执行,产生主线程对象,创建t1线程对象,有t1自己的栈,信息,我们创建t2线程对象,有t2的栈和信息。一旦被创建,就有被调动的可能性。没有睡眠函数,依旧可以调动。当我们执行t1线程,在ti空间调动funa函数,但是++的是对全局对象,++是全局量。
我们的g_max有可能加不到9
线程1和线程2同时对全局变量(比如是1)取值++了!然后一起把2返回了。对g_max++有三条汇编语句,不是原子操作。
这个全局变量被多个线程对象共享。要么进行原子性,要么进行加锁。
我们接着看。
如果线程函数参数是指针呢?
线程是可以这样的。
我们不能让主函数先死再执行线程funa,就变成失效指针了。
如果线程函数参数是引用呢?
接着往下看
类的成员函数线程化问题
执行一下
如果都对value值处理呢?
运行程序
我们传递的obj都是一个个的副本。
我们打印this指针看一下
当线程化t1的时候,传入obj,建立obj的副本,以此类推。
我们把构造函数和拷贝构造函数给出来看看
我们拷贝了3次函数。
我们将要线程化,obj构建临时对象参与。对value++是对副本++,不会影响obj。
这里的thread函数,第一个参数是线程化的函数名,第二个是对象名,第三个是传入的参数。
声明成静态函数,thread构建时候,不用传对象进去,因为静态函数没有this指针。
仿函数线程化
创建线程传入的参数对象取地址会是怎么样的?
加个&进去看看
把当前对象的地址传进去给this指针了。
线程之间不能互相赋值和拷贝
但是可以进行移动赋值!
移动赋值,资源转移!t2线程资源给t1,t2的线程释放了。
所以线程函数有移动构造函数和移动赋值函数
分离线程(detach)
线程的分离。
运行程序
主线程执行完毕。
程序进入主函数,我们分配了一个进程,有代码区,数据区,栈区,堆区。进入到主函数,分配主线程,在主线程创建t1线程,对应于线程对象t1,当我我们t1.detach的时候,t1和线程对象没关系了,主程序执行完毕,不管子线程是否执行完毕,都将撤销这个子线程的资源。
一旦分离意味着离婚了,没有关系了。
这种可能性也是存在的!
线程swap
t1资源和t2资源交换
如果t1没有资源,就是t2资源给t1
句柄相当于id号
返回线程的id号
主线程的ID号也可以打印。
当我们程序执行,进入主函数,给进程分配空间,创建主线程对象,填写主线程的信息包括ID号。子线程也是。
我们创建t1线程,有自己的栈空间,ID号。
我们创建t2线程,有自己的栈空间,ID号。
当我们继续执行,取t1,t2的ID号。这个全局函数get_id是运行在各自的线程空间中。我们可以认为是把当前线程信息传递进去的。
这个静态函数是返回内核CPU的个数。
但是打印的是12。
返回的是虚拟内核数
线程joinable函数
活跃和不活跃并不指的是你是否把程序执行完,指的是你的线程对象是否存在,如果线程对象存在就是活跃,如果线程对象不存在就是不活跃!
C11不可以获取线程返回值
没有办法通过retur获得!
C11没有给接口函数,我们只能继承C11然后增加函数。
线程加锁
原因是打印的时候线程互相侵占!
我们加入互斥器!
但是我们不能保证先打印a还是b,次序保证不了,保证的是线程1打印的时候其他线程不能进行打印!
RAII(资源获取即初始化)
我们创建对象,在对象里面加锁,函数执行完,要销毁,解锁。基于智能指针,构造函数用于获取资源,析构函数用于释放资源!