进程、线程和协程区分
我们通常所说的协程Coroutine其实是corporate routine的缩写,直接翻译为协同的例程,一般我们都简称为协程。
在linux系统中,线程就是轻量级的进程,而我们通常也把协程称为轻量级的线程即微线程。协程的本质是单线程,CPU不知道协程的存在,协程机制是在上层做的封装。
不管进程、线程还是协程,它们有一个共同的目的就是提高CPU的利用率,使得CPU尽可能少地空转。
进程和协程:
相同点:
相同点存在于,当我们挂起一个执行流的时,我们要保存的东西:
- 栈, 其实在你切换前你的局部变量,以及要函数的调用都需要保存,否则都无法恢复
- 寄存器状态,这个其实用于当你的执行流恢复后要做什么
而寄存器和栈的结合就可以理解为上下文,上下文切换的理解:
CPU看上去像是在并发的执行多个进程,这是通过处理器在进程之间切换来实现的,操作系统实现这种交错执行的机制称为上下文切换
操作系统保持跟踪进程运行所需的所有状态信息。这种状态,就是上下文。
在任何一个时刻,操作系统都只能执行一个进程代码,当操作系统决定把控制权从当前进程转移到某个新进程时,就会进行上下文切换,即保存当前进程的上下文,恢复新进程的上下文,然后将控制权传递到新进程,新进程就会从它上次停止的地方开始。
不同点:
- 执行流的调度者不同,进程是内核调度,而协程是在用户态调度,也就是说进程的上下文是在内核态保存恢复的,而协程是在用户态保存恢复的,很显然用户态的代价更低
- 进程会被强占,而协程不会,也就是说协程如果不主动让出CPU,那么其他的协程,就没有执行的机会。
- 对内存的占用不同,实际上协程可以只需要4K的栈就足够了,而进程占用的内存要大的多
- 从操作系统的角度讲,多协程的程序是单进程,单协程
线程和协程:
既然我们上面也说了,协程也被称为微线程,下面对比一下协程和线程:
- 线程之间需要上下文切换成本相对协程来说是比较高的,尤其在开启线程较多时,但协程的切换成本非常低。
- 同样的线程的切换更多的是靠操作系统来控制,而协程的执行由我们自己控制。
协程只是在单一的线程里不同的协程之间切换,其实和线程很像,线程是在一个进程下,不同的线程之间做切换,这也可能是协程称为微线程的原因吧。
协程的优缺点:
协程的好处:
- 无需线程上下文切换的开销
- 无需原子操作锁定及同步的开销方便切换控制流,简化编程模型(所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序是不可以被打乱,或者切割掉只执行部分。视作整体是原子性的核心。)
- 高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。
缺点:
- 无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。
- 进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序
协程的标准操作模块:
- greenlet
- gevent
1. greenlet协程
此模块可以实现协程,但是需要手动切换,因此用的不多。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import greenlet
"greenlet可进行手动切换"
def test1():
print(12)
gr2.switch() # 启动gr2
print(34)
gr2.switch() # 切换回gr2
def test2():
print(56)
gr1.switch() # 切换gr1
print(78)
gr1 = greenlet.greenlet(test1) # 启动一个协程
gr2 = greenlet.greenlet(test2)
gr1.switch() # 启动/切换
2. gevent协程
Gevent是一种基于协程的Python网络库,它用到Greenlet提供的,封装了libevent事件循环的高层同步API。它让开发者在不改变编程习惯的同时,用同步的方式写异步I/O的代码。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import gevent
def func1():
print('