11 | 多任务:进程、线程与协程
11 | 多任务:进程、线程与协程
讲述:丁伟
时长16:04大小14.80M
多任务与执行体
进程与线程
协程与 goroutine
架构师的批判性思维
结语
赞 41
提建议
精选留言(94)
- 易林林2019-05-21这才是真正的架构师课程,如果不具备这些基础知识,很难想象能够设计出好的软件系统架构。现在看到很多懂点技术和懂点产品的人自封为架构师,并没有去真正抓住软件架构的本质,实在感到有些汗颜。共 2 条评论82
- 一步2019-05-21对于协程的概念没有理解,协程不也是走系统调用吗?走系统调用不就是走到了系统内核态呢?后面任务调度,cpu执行指令
作者回复: 协程不走系统调用。协程切换只是寄存器的保存和恢复,所以可以在用户态下自己来实现。
共 3 条评论40 - Barry2019-05-21有一个小建议,能否再每篇文章的最后面预告一下,下一篇要讲的主题。这样我们跟着主题先思考,等看文章的时候就可以看到作者和自己的想法有什么出入和补充。更有利于吸收
作者回复: 挺好的建议,多谢。下一节我们讲 “进程内协同:同步、互斥与通讯”。
32 - Linuxer2019-05-21有一个疑问:协程属于用户态的线程,它跟线程之间怎么对应呢?协程之间也需要切换,那线程切换的那些成本它一样有啊,没想明白它的优势在哪
作者回复: 从单位时间成本来说,有一定优势但也不会特别大。主要少掉的代价是从用户态到内核态再回到用户态的成本。这种差异类似于系统调用和普通函数调用的差异。因为高性能服务器上io次数实在太多了,所以单位成本上能够少一点,积累起来也是很惊人的。
共 5 条评论30 - 饭2019-07-29老师,可以这样理解吗?因为时代背景久远,当初操作系统设计的线程,不太适应现在巨流量的互联网时代,在网络IO请求过高的情况下,性能开销太大,所以才出现了协程的概念,还有一些线程池的手段来弥补这个问题
作者回复: 应对方式有两种:一种是经典的线程池+异步io,一种是基于协程的同步io。后者背后的原理也是线程池+异步io,只不过加上了协程的语法糖了。
共 3 条评论20 - youyui2019-05-29想了解下协程如何操作寄存器切换CPU上下文的,有没有什么好的资料可以学习下
作者回复: https://github.com/Tencent/libco
共 3 条评论19 - 王聪 Claire2019-05-24您好,问一下epoll的意义在于让线程数量变少,是指等待执行的线程变少了吗?是因为都登记然后才能执行的机制吗?还是其他原因呢?谢谢。
作者回复: 如果用同步 io,那么每个并行 io 必然需要需要一个线程。epoll 在于让 io 等待都发生在相同的地方,相当于线程做了多路 io 复用。
共 3 条评论17 - 孙梦华🙄🙄2021-04-02操作系统所有涉及系统调用的方法都在内核空间,包括磁盘读写,内存分配回收,网络接口读写数据,这些都是web应用巨频繁使用的。 如果是多线程,线程在进行io操作时需要从用户态切换到内核态,等待io的过程中要进行内核态线程的切换,然后再从内核态回到用户态,时间和空间的开销都很大。 go实现的协程里面,如老师讲的,是用户态执行体和独立的io子系统,相当于用户空间的线程和内核空间的线程分隔开,互不进出,用户态的线程执行到io操作时,通过epoll的形式登记一个io请求,内核线程执行完io请求以后其实逻辑上是调用用户态的回调方法,然后这里go把这种反人类的异步回调模式,给我们程序员封装起来了。 是不是可以这样理解啊展开
作者回复: 是这样,理解到位
16 - 闫飞2019-06-06这里对NodeJS部分的引述有点小错误,它本身厉害的地方应该是争取了很多不想学习底层C/C++语言又喜欢得到高性能的"懒惰"的程序员。它的底层是由v8解释器和基于用户代码单事件循环调用的libev调度框架组成。 可惜的是遇到复杂的事件循环堵死的情况,不理解这些底层原理的流水线JS程序员依然不能很快找到解决方案。碰到一些封装了第三方C库的运算复杂度高的代码,FFI接口反而隐藏了更大的问题。 至于异步编程回调麻烦的问题,其实编程语言也在另外一个纬度上封装异步原语,通过promise/await等高级抽象缓解回调地狱困境。展开12
- 钱晟龙🐲龍🐉2019-05-29老师,我一直有个问题没理解到,计算机在做IO的时候会不会使用CPU,如果会怎样使用的? 阻塞IO阻塞的时候,也就是IO进行时,它对应的线程是否已经放弃了CPU的执行权? 或者老师建议我查阅什么书籍。。
作者回复: 1、https://m.baidu.com/sf_edu_wenku/view/3210fec818e8b8f67c1cfad6195f312b3169ebe8 2、是的,执行权会转移
11 - 王棕生2020-05-06我理解: 线程是CPU调度的基本单位,进程是资源(包括CPU计算资源)分配的基本单位;操作系统在决定谁来使用CPU的时候,操作系统不会去关注进程,而是关注线程,只有要切换到其它进程的线程时,才会关注进程。 比如:进程p1有三个线程t1 t2 t3,进程p2有三个线程t4 t5 t6, 操作系统关注的是这6个线程的调度,比如从t1切换到t2,从t2切换到t3,当从t3切换到t4的时候,发现t4是属于另一进程的,这个时候就会不知线程要调度,进程也要调度。 麻烦许老师点评一下,这样理解是否准确?展开
作者回复: 我们想一下进程的本质是什么。我们以内存资源为例,内存在不同进程中的逻辑地址与物理地址的映射表不同,更具体来看其实就只是几个寄存器的值的差异而已。所以实际上,并不存在线程调度和进程调度的区别,线程调度了,寄存器变了,进程也就切换了。
共 2 条评论9 - 136019946252019-06-19理论上协程可以做到的优化,线程都可以做到。为什么不在操作系统层去解决这个问题呢?
作者回复: 但是操作系统太多了,语言适配操作系统易,反过来难
7 - 行者2019-05-23不太理解协程的是怎么做到文中提到的两个优势的。只是看了一下python的协程,能理解它能减少执行体切换的时间成本(因为全在用户态中),但它的执行本质上就是串行执行呀,只是不同的子程序有了更多的入口而已,那怎么做到加速呢?
作者回复: python的协程是比较狭义的,它只是一种编程模式,并不算执行体。你可以了解一下Go语言的goroutine。
5 - Geek_03056e2019-05-21有几个问题请教一下老师: 1 cpu时间片运行执行体,选择执行体时,是cpu控制,还是操作系统控制?进程、线程、协程获得的概率是一样的吗? 2 通过sh,cpu知道了进程的首地址,执行进程,这个线程是怎么执行的呢? 3 文中提到网络服务器的存储是个共享状态,这个存储指哪些存储呢?
作者回复: 1、文章中有表格,里面的调度方就是你说的控制方。不同执行体控制方不一样,调度算法不一样,概率也就不一样。 2、线程知道函数地址就行,一样是入口。 3、共享存储包括内存、数据库等等。
5 - 大糖果2019-05-21那可以理解为如果操作系统把线程实现的足够灵活,轻便,就不需要协程这个机制了吗?
作者回复: 我认为是这样
共 2 条评论5 - Geek_88604f2020-07-20为了改进网络服务器的吞吐能力,现在主流的做法是用 epoll(Linux)或 IOCP(Windows)机制,这两个机制颇为类似,都是在需要 IO 时登记一个 IO 请求,然后统一在某个线程查询谁的 IO 先完成了,谁先完成了就让谁处理。——这里面有一个疑问,假设线程1已经登记了A和B两个IO请求,线程1正在处理请求C,这个时候A的IO已经准备完了,此时线程1是先登记C还是先处理A?
作者回复: io完成只会做个标记,不会发生具体的动作。需要有一个地方去查询哪些io已经完成了,这个查询可以发生在线程1,也可以在任意其他线程。对于那些已经完成的io,往往会触发一个回调函数,这就是常规的异步io模型(基于回调函数)。在Go里面回调函数变成了goroutine切换,其他完全一样。
共 2 条评论4 - Wilson2019-12-28“系统中有大量的 IO 请求,大部分的 IO 请求并未命中而发生调度。”老师,这句话我不是很理解,能解释一下吗
作者回复: 在高并发的网络服务器中,有大量的执行体都处在 io 请求中,并且 io 的数据还没有准备好,所以这些执行体 io 请求不可完成,只能让出 cpu 时间。
共 5 条评论4 - Sylh2019-05-28https://www.nginx.com/blog/inside-nginx-how-we-designed-for-performance-scale/ 官方文档里面的,我看就是说是多进程的
作者回复: https://linux.cn/article-5681-1.html?pr 你可以仔细看一下中文版的
4 - 靠人品去赢2019-05-21从来没有看过将CPU内部的寄存器的角度来看待程序的执行,比如A切换到B,保存A的寄存器值(貌似文中有一次笔误,保存寄存器应该是保存寄存器的值。)然后恢复B上一次寄存器的值。这个切换的过程结束怎么判定? 是不是根据虚拟内存页转换的物理内存页的数据,读完写完就完事了。感觉这个寄存器开始来看操作系统的调度,终于从这个死记硬背进程线程概念更明白了些,包括CPU寄存器怎么工作(之前计算机原理学的不扎实,只知道里面都是这个东西而已)。展开
作者回复: > 这个切换的过程结束怎么判定? 最后一个切换的寄存器是指令执行的当前位置(EIP),改了 EIP 后,CPU 就跳转到新的地方执行,进程切换了。
共 3 条评论4 - NiceBlueChai2021-05-21为啥操作系统不把自己的线程堆栈搞成按需调整大小的
作者回复: 有很多背景因素,包括这种按需调整大小和gc也有一定的关联,会导致非gc类语言实现不易。不过我觉得简易版本的实现是有可能的,比如保留地址空间但不分配内存,这样至少可以做到按内存页(64K)为单位来增长,而不需要因为地址变化导致程序逻辑不正确。
3