用户级多线程的切换原理

前言

上文(从多进程到多线程)中,我们讨论了何为多线程,而线程又分用户级线程和内核级线程,这节我们先来讨论一下何为用户级线程以及用户级线程的底层原理

用户级线程和内核级线程

说到用户级多线程,我们有必要先来简单对比一下用户级线程和内核级线程之间的区别。

内核级线程,顾名思义,它的调度是依赖于操作系统的,即操作系统控制着内核级线程的切换,比如有A和B两个内核级线程,我们用户是不知道先执行哪个线程的代码和不知道什么时候切换到另一个线程执行代码的,这件事只有操作系统知道,我们无法干预。

用户级线程,顾名思义,它的调度是依赖于用户的想法的,比如有C和D两个用户级线程,我们用户可以先让A执行一段代码后,然后手动控制让其跳到B去执行一段代码,我们是清楚知道线程间的切换的。

简单一句话来说就是:内核级线程是由操作系统进行调度的,用户级线程是由用户来控制调度的。

用户级线程

我们举例子,来进一步说明用户级线程切换的底层原理,还是记住那句话:用户级线程的切换是由我们用户来主动控制的。

现在我们假设有线程1和线程2两个线程(图中红色的数字为内存的地址)

可以看出,线程1中有A()和B()两个函数,执行流程为A()函数调用B()函数,B()函数执行完毕后返回到地址为104的语句继续往下执行;线程2中有C()和D()两个函数,执行流程为C()函数调用D()函数,D()函数执行完毕后返回到地址为304的语句继续往下执行。那么图中还有一个Yield()函数到底是什么东西呢,简单来说它就是我们用户主动来控制线程切换的一个函数,在线程1中调用Yield()函数,此时会切换到线程2,在线程2中调用了Yield()函数,此时又会回到线程1继续执行。因此,执行流程为下图所示。

现在我们更加深入地去剖析整个切换过程到底发生了什么有趣的事。

线程1运行:B() 为函数调用,此时将函数调用的下一条指令地址入栈,即104入栈(因为要记录函数调用结束后返回到哪里继续执行)。

随即进入B()内执行 ——> 线程切换(用户级线程)函数Yield()也为函数调用(特殊函数调用),即将要执行Yield(),则204入栈,随即执行Yield() ——> 跳至内存中地址为300的指令执行,此时即进行了线程切换。

类似,执行C() ——> 调用D(),304入栈,即将执行Yield(),404入栈。

随即执行线程2的Yield() ——> 切换至线程1执行204 ,之后B() 结束,弹栈,为404 ,即执行完后跳去地址为404处执行。而实际上我们开始说过,执行完B()后要按轨迹5返回线程1的地址为104处执行,而此时调用执行完进入线程2的404,出错。

某线程中的普通函数调用(Yield是特殊函数调用)只能在本线程来回折腾,所以上述情况的普通函数调用会返回到另外的线程执行,这是错误的。

出错原因是因为两个线程共用了一个栈,导致线程之间切换和内部运行出现错乱。

因此,可以用两个栈解决这个问题,即分别为每一个线程分配一个独立的栈!

还是上面的那个例子

此时的栈不在是上面的那仅有的一个栈,而是线程1有一个栈,线程2也有一个栈,其中它们各自的栈地址存放于各自线程的TCB中,还有一个全局的栈指示变量,它的作用是指示当前用的是哪一个线程的栈。

那么,线程1运行:B() 为函数调用,此时将函数调用的下一条指令地址入栈,即104入线程1中的栈,B()函数中执行Yield()函数,204入线程1中的栈,然后切换到线程2执行。

线程2执行,同理,304入线程2的栈,404入线程2的栈。

此时,两个栈的状态如下图

线程2执行Yield()函数后,全局栈指示变量指向线程1的栈,此时执行出栈操作,出的是线程1的栈,弹出204,转到204执行语句,接着B()函数调用完后,return,继续在线程1的栈中(因为全局栈指示变量依然指向线程1的栈)执行出栈操作,弹出104,转回A()函数中的104语句,此时,就完美地解决了使用一个栈会导致乱跳转的问题!

这一节,我们讲述了内核级线程和用户级线程的基本概念与区别、用户级线程的切换底层原理

本文分享自微信公众号 - IT界的泥石流(gh_888495c5eb2c)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-07-08

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏侯哥的Python分享

python基础-变量运算符(3)

注释就是对代码的解释和说明。目的是为了让别人和自己很容易看懂。为了让别人一看就知道这段代码是做什么用的。正确的程序注释一般包括序言性注释和功能性注释。序言性注释...

13420
来自专栏Python绿色通道

项目实战 | 手把手做一款小说阅读器

前一段时间书荒的时候,在喜马拉雅APP发现一个主播播讲的小说-大王饶命。听起来感觉很好笑,挺有意思的,但是只有前200张是免费的,后面就要收费。一章两毛钱,本来...

22420
来自专栏攻城狮的那点事

为何人工智能 AI 首选Python 读完这篇文章你就知道了

为何人工智能(AI)首选Python?读完这篇文章你就知道了。我们看谷歌的TensorFlow基本上所有的代码都是C++和Python,其他语言一般只有几千行 ...

12530
来自专栏Python绿色通道

骚操作!用Python把公众号文章打包成pdf文件,不再怕自己的劳动成果被丢失

做自媒体的人,尤其是做了一年甚至更久的自媒体人,尤其是通过自媒体还有一些小收入的人,他们最怕自己的公众号内容因为各种原因而丢失,那就太可怕了! 在做自媒体内容上...

15420
来自专栏侯哥的Python分享

工欲善其事,必先利其器-Python编辑器选择(2)

一款顺手的好的编辑器可以让程序员写代码更得心应手,效率也会更高,但是编辑器本身没有好坏,只有使用者使用起来是否顺手而已,这里简单给大家介绍几款常用的可以编辑Py...

10440
来自专栏快乐学Python

PyCharm在创建py文件时自动添加默认头部注释

File ----->> Setting ----->> Editor ----->> File and Code Templates ----->> Pyth...

15830
来自专栏快乐学Python

celery时差问题解决方法

请记得点赞和分享这篇文章让更多的人看到它!另外,记得关注我的简书号马哥学Python,这样你就不会错过任何有价值的文章!

10420
来自专栏程序员小王

菜鸟学Python——初识Python

很多初学Python的同学经常问我这样的问题:学Python应该看什么书啊?我会非常自信的把之前整理的Python教程扔给他,后来收到很多反馈:你的排版太烂了...

23130
来自专栏侯哥的Python分享

python算法与数据结构-双向链表(40)

  一种更复杂的链表是“双向链表”或“双面链表”。每个节点有两个链接:一个指向前一个节点,当此节点为第一个节点时,指向空值;而另一个指向下一个节点,当此节点为最...

15560
来自专栏快乐学Python

Pycharm 修改自动格式化时每行的字数

9840

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励