进程线程协程总结
一句话
CPU 占用时间间隔的单位
通俗易懂的例子
-
计算机的核心是 CPU,它承担了所有的计算任务。它就像一座工厂,时刻在运行。
-
假定工厂的电力有限,一次只能供给一个车间使用。也就是说,一个车间开工的时候,其他车间都必须停工。背后的含义就是,单个 CPU 一次只能运行一个任务。
-
进程就好比工厂的车间,它代表 CPU 所能处理的单个任务。任一时刻,CPU 总是运行一个进程,其他进程处于非运行状态。
-
一个车间里,可以有很多工人。他们协同完成一个任务。
-
线程就好比车间里的工人。一个进程可以包括多个线程。
-
车间的空间是工人们共享的,比如许多房间是每个工人都可以进出的。这象征一个进程的内存空间是共享的,每个线程都可以使用这些共享内存。
-
可是,每间房间的大小不同,有些房间最多只能容纳一个人,比如厕所。里面有人的时候,其他人就不能进去了。这代表一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。
-
一个防止他人进入的简单方法,就是门口加一把锁。先到的人锁上门,后到的人看到上锁,就在门口排队,等锁打开再进去。这就叫"互斥锁"(Mutual exclusion,缩写 Mutex),防止多个线程同时读写某一块内存区域。
-
还有些房间,可以同时容纳 n 个人,比如厨房。也就是说,如果人数大于 n,多出来的人只能在外面等着。这好比某些内存区域,只能供给固定数目的线程使用。
-
这时的解决方法,就是在门口挂 n 把钥匙。进去的人就取一把钥匙,出来时再把钥匙挂回原处。后到的人发现钥匙架空了,就知道必须在门口排队等着了。这种做法叫做"信号量"(Semaphore),用来保证多个线程不会互相冲突。
从计算机本质上讲
参考: 线程和进程的区别是什么?
本质 :CPU 一个核心在一个时间点只能做一件事,线程进程的定义是 CPU 时间(片)段的描述——也叫 CPU 时间片
原因 :CPU 太 TM 快了
上下文 : 其他相关资源 内存,显卡,GPS …
程序执行流程 : 程序 A 得到 CPU (A 说:CPU 现在是我的了!)—— CPU 加载上下文(A 相关的)——CPU 执行 A ——执行完毕,保存上下文,释放 CPU(可以继续接客了)
一个程序有多个可以同时执行的逻辑 : 程序 A 得到 cpu —— Cpu 加载上下文 —— (执行 A 的 a 段逻辑 | 执行 A 的 b 段逻辑|执行 A 的 c 段逻辑) —— 执行完成,保保存上下文,释放 CPU
并行和并发 :其中,如果有多核 CPU 服务,a、b、c 就可以并行执行(多个 cpu 同时做多件事);如果是单核——只有一个人服务,那就是并发执行(一会儿干 a,一会儿干 b,一会儿干 c…)
进程
- 强调一个运行的状态:编写完毕的代码,在没有运行的时候,称之为程序。正在运行着的代码,就成为进程。
- 操作系统分配的一个基本单位:运行一个程序,就创作一个进程,操作系统分配资源。
- 隔离和共享: 进程内部共享资源,进程间不能直接共享资源
线程
- 线程是 CPU 调度和分派的基本单位:是进程中执行运算的最小单位,亦即执行处理机调度的基本单位。如果把进程理解为在逻辑上操作系统所完成的任务,那么线程表示完成该任务的许多可能的子任务之一。
- 它可与同属一个进程的其他的线程共享进程所拥有的全部资源,
- 常常多个线程共享资源时要加互斥锁,避免数据竞争状态的出现
进程线程对比
参考:进程与线程,单核与多核
联系:
- 一个线程只能属于一个进程,而一个进程可以有多个线程,至少有一个线程。
- 资源分配给进程,同一进程的所有线程共享该进程的所有资源。
- 处理机分给线程,即真正在处理机上运行的是线程。
- 线程在执行过程中,需要协作同步(加锁,通道)。不同进程的线程间要利用消息通信的办法实现同步。线程是指进程内的一个执行单元,也是进程内的可调度实体。
区别:
- 资源单位 1 :线程作为调度和分配的基本单位,进程作为拥有资源的基本单位
- 资源 :进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源。
- 并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行
- 开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。
不同
线程和进程对比
- 地址空间:线程是进程内的一个执行单元,进程内至少有一个线程,它们共享进程的地址空间,而进程有自己独立的地址空间
- 资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源
- 线程是处理器调度的基本单位,但进程不是
- 二者均可并发执行
- 每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口,但是线程不能够独立执行,必须依存在应用程序(进程)中, 由应用程序提供多个线程执行控制
进程间通信
- 消息队列
- 管道 pipeline
- socket:http 等
协程
协程,又称微线程,纤程。英文名 Coroutine。
协程是啥
首先我们得知道协程是啥?协程其实可以认为是比线程更小的执行单元。 为啥说他是一个执行单元,因为他自带 CPU 上下文。这样只要在合适的时机, 我们可以把一个协程 切换到另一个协程。 只要这个过程中保存或恢复 CPU 上下文那么程序还是可以运行的。
通俗的理解:在一个线程中的某个函数,可以在任何地方保存当前函数的一些临时变量等信息,然后切换到另外一个函数中执行,注意不是通过调用函数的方式做到的,并且切换的次数以及什么时候再切换到原来的函数都由开发者自己确定——写的代码确定
协程和线程差异
那么这个过程看起来比线程差不多。其实不然,线程切换从系统层面远不止保存和恢复 CPU 上下文这么简单。 操作系统为了程序运行的高效性每个线程都有自己**缓存 Cache **等等数据,操作系统还会帮你做这些数据的恢复操作。 所以线程的切换非常耗性能。 但是协程的切换只是单纯的操作 CPU 的上下文,所以一秒钟切换个上百万次系统都抗的住。
协程的问题
但是协程有一个问题,就是系统并不感知,所以操作系统不会帮你做切换。 那么谁来帮你做切换?——用户,写代码的。 让需要执行的协程更多的获得 CPU 时间才是问题的关键。
例子
目前的协程框架一般都是设计成 1:N 模式。
所谓 1:N 就是一个线程作为一个容器里面放置多个协程。
那么谁来适时的切换这些协程?答案是有协程自己主动让出 CPU,也就是每个协程池里面有一个调度器, 这个调度器是被动调度的。意思就是他不会主动调度。而且当一个协程发现自己执行不下去了(比如异步等待网络的数据回来,但是当前还没有数据到), 这个时候就可以由这个协程通知调度器,这个时候执行到调度器的代码,调度器根据事先设计好的调度算法找到当前最需要 CPU 的协程。 切换这个协程的 CPU 上下文把 CPU 的运行权交个这个协程,直到这个协程出现执行不下去需要等等的情况,或者它调用主动让出 CPU 的 API 之类,触发下一次调度。
那么这个实现有没有问题?
其实是有问题的,假设这个线程中有一个协程是 CPU 密集型的他没有 IO 操作, 也就是自己不会主动触发调度器调度的过程,那么就会出现其他协程得不到执行的情况, 所以这种情况下需要程序员自己避免。这是一个问题,假设业务开发的人员并不懂这个原理的话就可能会出现问题。
协程的好处
-
多线程中的问题 在 IO 密集型的程序中由于 IO 操作远远慢于 CPU 的操作,所以往往需要 CPU 去等 IO 操作。 同步 IO 下系统需要切换线程,让操作系统可以在 IO 过程中执行其他的东西。 这样虽然代码是符合人类的思维习惯但是由于大量的线程切换带来了大量的性能 (CPU) 的浪费,尤其是 IO 密集型的程序。
-
异步 IO 解决上问题,引入新问题 所以人们发明了异步 IO。就是当数据到达的时候触发我的回调。来减少线程切换带来性能损失。 但是这样的坏处也是很大的,主要的坏处就是操作被 “分片” 了,代码写的不是 “一气呵成” 这种。 而是每次来段数据就要判断 数据够不够处理哇,够处理就处理吧,不够处理就在等等吧。这样代码的可读性很低,其实也不符合人类的习惯。
-
协程近乎完美解决问题(一点点缺陷) 但是协程可以很好解决这个问题。比如 把一个 IO 操作 写成一个协程。当触发 IO 操作的时候就自动让出 CPU 给其他协程。要知道协程的切换很轻的。 协程通过这种对异步 IO 的封装 既保留了性能也保证了代码的容易编写和可读性。在高 IO 密集型的程序下很好。但是高 CPU 密集型的程序下没啥好处。
协程和线程比较
- 一个线程可以多个协程,一个进程也可以单独拥有多个协程
- 线程进程都是同步机制,而协程则是异步
- 协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态
其他常见问题
-
什么是线程安全? 如果多线程的程序运行结果是可预期的,而且与单线程的程序运行结果一样,那么说明是“线程安全”的。
-
线程的特征:
- 轻量,暂用资源少
- 调度的基本单位,切换线程消耗资源小于进程
- 可并发
- 共享进程资源
-
进程线程优势对比?
- 原文作者:战神西红柿
- 原文链接:https://tomatoares.github.io/posts/system/%E8%BF%9B%E7%A8%8B%E7%BA%BF%E7%A8%8B%E5%8D%8F%E7%A8%8B/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。