背景 Background
早期计算机中的 CPU 比较弱,通常执行一个任务
后才能执行下一个任务
,效率特别低。
后来随着计算机相关工业的发展, CPU 的速度提升了,内存也增大了,计算机能干的事也越来越多了,要处理的任务也跟着增加了,一次只能执行一个任务的做法显然是不够用。怎么办?
那我们就让计算机同时执行多个任务吧,但 CPU 只一个(当时只有单核),那就先执行 Task 1 一段时间,然后执行 Task 2 一段时间。这样消费者看起来像是同时执行了两个 Task。
一切都是想起来很美,但现实往往过于真实,让人不忍直视。
CPU 可以在 Tasks 间切换,但内存不行,一旦内存被 Task 1 使用了,如果 Task 2 再使用相同
内存地址,要报 dirty read error,要么把 Task 1 里的数据覆盖了。如果是你来解决这个问题,你会怎么设计?
对,让 Task 2 不读 Task 1 的内存地址。那么怎么让计算机知道呢?
那我们就从内存下手, 如果 Task 1 使用了 x0000 ~ x01000(假设值)这一段,我们就标记为 task1。 当 Task 2 要使用内存 x01000 ~ x05000 我们就将这一段标记为 task2,后面 Task 3 ~ Task n 的同理可推。
有了这些标记,OS 就知道哪段内存正在被 Task 几使用,新来的 Task 就会申请后面的内存,这样就算切换任务也不会受影响。
进程 Process
当操作系统发现一个正在运行的进程正在执行某项耗时的操作,通常会将 CPU 的执行权交给另一个进程,虽然现代操作系统采用了时间分片
策略。
一个进程,可能会有多种操作,如:网络请求,文件读写,打开关闭数据库等,在这段空闲的时间里,OS 通常会把 CPU 使用权交给别的进程,这样就会造成大量的时间花在进程间切换,并且这种切换是由 OS 来进行管理,开发人员心有余而力不足,怎么办?
线程 Thread
那我们就在进程的基础上再抽象出一层出来 -- 线程
,这时一个进程可以派生多个线程,它们还可以选择共享或不共享逻辑资源。
最宁人兴奋的是开发个人可以自己控制线程,但别高兴的太早,如果对线程控制不当
,也会造成混乱,从而影响数据有效性。好消息是就算出错了,也只在进程内部,相关于有效的控制了出错的范围。 如果开发人员控制得当
,那就会高效的调用 CPU 资源,从而提高生产力,消费者爽得一批。
协程 Coroutine
这个技术名词音同某家大数据杀熟
的公司,在此谴责和鄙视一下他们,推荐大家用我前东家「艺龙」服务。
协程提供了一种协作式的多线程(升级啦),每个协程者可以看作一个线程
,区别是协程是彼此交错
运行的,这表示在某个时间点只能有一个协程在运行
。
协程也可以理解为当前 Context(执行上下文),Context 可以是寄存器的值、当前使用的内存地址等。
协程的作用是在不同 Task 间切换,这和 OS 的调度非常像。但不同点是 OS 的执行策略是抢占式
,受 OS 调度控制,而协程的 Task 的调度是交给自身来完成。
总结
类别 | 特点 |
---|---|
远古时代 | 不存在 CPU 切换的工作、只有执行完当前 Task,才能执行下一个 Task、CPU 利用率低,生产效率低。 |
进程 Process | OS 生成多个不同的进程,可同时执行多个 Task,CPU 利用率和生产效率明显提升,由 OS 来统一调度。如果有进程执行多个操作,需频繁切换时间分片,消耗部分 CPU 时间。一个进程出错整个程序就奔溃了。 |
线程 Thread | 进程可以派生多个线程,多个线程可以执行更多的 Task,开发人员来调度线程。如果某个线程出错,只会影响数据有效性,不会造成整个程序就奔溃。 |
协程 Coroutine | 协程者可看作特殊的线程,彼此交错运行,用于 Task 间切换,非抢占式,调度自身来完成。 |
网友评论