首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

一文读懂 线程 挂起

做为一个码农,在开发的时候,经常会使用到  Object.wait 等操作,挂起当前线程,当时我心里一直有个疑惑,这个挂起底层到底是如何实现的呢?

要想理解线程挂起,我们得先明白线程是如何被执行的,当程序运行之后,系统会创建一个进程,进程是一个资源单位,代表程序可以使用的资源,而线程才是真正的执行单位,参与操作系统的调度

每个线程都有一个 task_struct 结构体(简称PCB),当然也可以理解为一个对象,这个结构体会被操作系统添加到一个队列中(简称 调度队列 ),这个结构体如下

操作系统给每一个线程PCB中参数赋值, self_kstack 保存了我们的编写的运行函数地址,表示cpu要执行那个函数, ticks 代表这个线程可以使用多少cpu时间,比如 ticks  = 5 ... 等,  那么操作系统是如何知道线程使用了多少CPU时间呢 ?

这里会涉及一些中断的知识,我们只是想了解线程是如何被执行,被挂起的,所以这里就不细讲中断是什么了,暂时先可以理解为是一个操作系统的函数(简称 调度函数)。

假设每隔10ms,cpu就切换到这个调度函数,调用前操作系统会替我们保护好当前线程执行的代码位置等一些参数到 self_kstack,然后在调度函数中,取出当前正在执行的线程,把该线程PCB中的 ticks 减 1 , 然后通过 self_kstack 还原线程参数和执行代码位置,继续执行。

如果减1之后,该线程的 ticks 等于 0 ,那么操作系统会从 调度队列头部 取出一个新PCB,然后去执行这个PCB中 self_kstack 指向的函数地址和代码位置,然后把当前线程的PCB放到队列尾部(注意,此时当前线程的PCB中还记录着当前的代码位置...等一些参数)。

就这样,每隔50ms就切换一个线程不间断的轮流执行,就实现了简单的任务调度

那么,线程挂起又是怎样的实现的呢? 其实很简单,我们知道想要线程执行,必须把线程的PCB放入调度队列,才有机会被cpu调度。如果我们不把当前线程的PCB放到调度队列,那么这个线程就永远不会被执行,不就是被挂起了吗? 有的同学可能会问,线程没在调度队列里,那么如果想唤醒这个线程的话,该怎么办?

这也是我们使用 Object.wait / Object.notify 的时候,为什么必须要加锁的原因,一个锁结构有下列字段

value字段,代表这个锁是否被别人持有,等于1的话代表空闲,waiters 是一个等待队列,存放线程的PCB。

我们在一个加锁的代码块里执行 Object.wait 的时候,操作系统会把当前线程的PCB从调度队列中移除,然后放入当前锁的等待队列中,所以当前线程被挂起。

显而易见,当在一个加锁的代码块里执行Object.notify的时候,操作系统从当前锁的等待队列中,取出一个PCB,加入到调度队列的首部,这样当前线程会很快的被cpu调度,所以线程被唤醒

通过添加/移除调度队列中的PCB,实现线程的挂起和唤醒,是不是很容易理解呢?

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20201231A0402W00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券