用户级线程 + 内核的LWP = Linux线程
OS概念中经常说的
用户级线程 和 内核级线程 也就是线程实现真的是在OS内部实现,还是应用层或用户层实现
很明显Linux是属于用户级线程
用户级执行流(用户级线程) :内核lwp = 1 : 1
也有1:n的但我们今天不管了
下面来谈谈线程库中的栈
这个栈不仅仅要简单的变量定义,入栈出战
每个执行流本质都是一条调用链
栈结构本质是为了支持应用层完成整个函数调用链所对应的
临时变量空间的开辟和释放
所以主线程当然要有自己的调用链
新线程在执行流上和主线程是独立的,所以他们形成调用链时
必定每一个人都要有自己独立的栈结构,让自己的调用链不受别人的
干扰,所以每一个线程都要有自己的栈结构。
站在线程角度每个线程都有自己独立的栈结构,但并不是说我
想访问你这个栈我就访问不了,其实有办法
创建多线程
把pthread_create套进循环里,每次形参的tid用vector来管理起来
也方便后续等待
创建线程给线程入口函数传参时,可不可以传入for循环中的临时变量?
肯定是不可以的,for循环结束 td变量也就释放了,你传的是临时变量的地址,你不能让线程指向主线程的栈而且还是一个临时变量,传参很大可能会翻车。
用全局变量传参呢?可能就不只需要定义一个因为是多线程
我们直接new出来一个数据对象给线程传参,这是比较原始的方法。
td变量指向的内容是堆空间的,每一次for循环都会重新创建堆空间
所以每个线程都可以访问属于自己的堆空间。
(也可以用容器,容器的本质不也是在堆上开辟吗,那套的层就多了,麻烦。)
从这个例子可以看出,所以线程一定是共享堆空间的,要不然我随便传给你一个堆空间上的对象怎么能传给你?
说明堆空间其实被大家线程共享,只不过我们一人一个罢了
我们不要互相干扰,如果今天我就是想让线程1访问线程2 的堆空间
能做到吗?绝对可以
我们把所有堆空间指针放到一个数组里保存起来,我不就可以访问了。
但我们不这么做。
我们一个线程拿着属于自己的一个指针就走了
进程内的每个线程本来就是一家人资源互相访问很正常。
所有的线程,执行的都是threadRoutine这个函数,这个函数就是被重入了。
问题:每个线程都要形成test_i这个临时变量,那每个线程都执行同一个函数那这个临时变量互相会影响吗?
图中看到每一个线程的test_i地址都不一样
验证出了每一个线程都会有自己独立的栈结构!
问题:如果我主线程就是想访问第一个线程的test_i这个变量,在技术上能做到吗?
答案是可以的,因为新线程栈区在库里,依旧在地址空间里面
如果主线程想访问任意线程栈区数据依旧能访问
验证一下
可以利用全局定义的变量,和线程内线程名配合拿到某个线程的test_i地址,这样主线程就可以通过这个全局变量拿到了
其实想说的就是
其实线程和线程之间,几乎没有秘密
线程的栈上的数据,也是可以被其他线程看到并访问的。
虽然能访问甚至是修改都是可以的,但我们禁止这么做,无论堆区还是栈区每个线程只有自己知道它的虚拟地址,每个线程把自己的资源保护好,不要让别人随随便便拿走就可以了
所以每个线程都有自己独立的栈结构,但不敢说私有的栈结构
如果定义一个全局变量,然后让所有线程去访问它并且++,
那这个代码有问题吗?
是有问题的。
这个全局变量g_val 被多个线程共享访问,这个g_val叫共享资源
共享资源被多线程并发访问就有可能导致数据不一致问题
这个问题稍后讨论
把问题聚到一下
线程的局部存储
如果我线程想要一个私有的全局变量呢????
利用编译选项 __thread来定义,这个编译选项不是c/c++提供的,而是编译器提供的一个编译选项
它只能定义内置类型,不能用来修饰自定义类型
被它修饰的全局变量是储存在 库里面tcb线程控制块中的局部存储中。
这样每个线程都有自己私有的全局变量,而且每个线程的这个全局变量都是一个变量名
那这个私有全局变量有啥用?
1.例如定义变量存储pid,而不用频繁调用系统调用getpid
2.在深度调用链函数调用时,,不用把局部定义的变量传入深度函数之中,而是可以直接用这个私有全局变量。
分离线程
默认新创建的线程是joinable 需要被等待的 属性
如果不Join就会内存泄漏,因为它的资源不会释放
如果今天根本不关心线程把任务完成的如何,还要让主线程能腾出手不必等待了,就用pthread_detach 分离
pthread_detach 分离后
1 . 如果一个线程已经被分离了,我们不能join了,也不需要Join了
,如果join就会返回错误码,等待失败
2. == 线程执行完毕后==,原生线程库会自动释放线程
在库里把库创建的tcb,栈,底层的轻量级进程就是pcb,全都释放掉
剩下的页表,地址空间,代码数据你别管,这是人家进程的。
这个分离的工作应该由谁来做呢?
既可以由主线程来做,也可由每个线程自己来分离
注意是线程执行完毕以后释放线程资源,而不是调用就释放
新线程中分离时,中间这个sleep如果不加,如果新线程还没分离主线程就开始等待,那么
会偶发看到等待成功的现象
细节
分离后线程本来是能执行个5秒,但是为什么代码只输出了这么一点?
因为线程创建出来分离后,主线程立马开始join,分离后不能等待了,所以join直接出错立马返回,main函数也就跑完了所以进程也就结束了线程也没要结束。也就是主线程先退了
所以代码一定要保证主线程是最后退出,常见的是主线程永远不退出。
分离本质就是改线程是否是分离的属性
分离后线程依旧共享地址空间的资源,但是线程退出了和主线程没关系了
线程互斥
上面的话题 说过 定义一个全局变量,每个线程都能看到并修改这个变量,在默认情况下把这个全局变量叫做共享资源。
问题:
共享资源在被多线程并发访问时,有没有可能一个线程正在访问另一个线程就来读或者来写,进而因为另一个线程的读写修改会影响到我呢?
就会造成因为共享造成的数据不一致问题。
写一份代码,说一个场景,引入互斥的必要性
用多线程,模拟一轮抢票
有1000个座,就得卖1000张票,而不能卖出1001张
说白了就是一堆线程对全局变量做减减
肯定是票数大于0再去抢票减减,否则就是抢票完成线程退出
抢票是要花时间的,用usleep来充当时间花费
还要用Printf输出哪个线程抢到了哪个票
创建4个线程并发去访问同一个全局变量票数,大家都要对这个票数做减减
tickets就叫共享资源
最终发现抢票时竟然抢到了0,甚至-1,-2
为什么会出现这样的问题?
共享资源被多线程并发访问造成了数据不一致问题
造成这种问题肯定和多线程并发访问是有关系的
tickets- - 和判断 tickets > 0
无非每个线程执行代码时都要对变量做检测,做减减
有没有可能你正在做判断时,其他线程也来了
有没有可能你正在做tickets - - 时,其他线程也来了
多个线程都在并发访问,互相没有约束,你对ticktes做操作时,别人
也来访问了,就可能会造成数据不一致问题
我们先谈tickets - - 这个操作在多线程访问时是否是安全的问题
也就是对一个全局变量进行多线程并发 - - / ++操作是否是安全的
再来谈为什么票数会减到 - 2,虽然这两个关系不是直接相关,但是理解上面的问题,
这个现象也就能理解了。
并发访问全局变量++ – 肯定不安全