一、Python中的协程经历了很长的一段发展历程。其大概经历了如下三个阶段:
1、最初的生成器变形yield/send
2、引入@asyncio.coroutine和yield from
3、在最近的Python3.5版本中引入async/await关键字
4、文章链接:[<u>https://cuiqingcai.com/6160.html</u>](https://cuiqingcai.com/6160.html)
二、什么叫协程?
协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方(线程调
度时候寄存器上下文及栈等保存在内存中),在切回来的时候,恢复先前保存的寄存器上下文和栈。
因此:
协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进
入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。
子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B,B在执行过程中又调用了C,
C执行完毕返回,B执行完毕返回,最后是A执行完毕。
所以子程序调用是通过栈实现的,一个线程就是执行一个子程序。
子程序调用总是一个入口,一次返回,调用顺序是明确的。而协程的调用和子程序不同。
协程看上去也是子程序,但执行过程中,在子程序内部可中断,然后转而执行别的子程序,在适当的
时候再返回来接着执行。在一个子程序中中断,去执行其他子程序,不是函数调用,有点类似CPU的
中断。
三、协程解决这样几个问题:
1、采用同步的方式去编写异步的代码,使代码的可读性高,更简便。
2、使用单线程去切换任务(就像单线程间函数之间的切换那样,速度超快)
意味着我们需要一个可以暂停的函数,对于此函数可以向暂停的地方穿入值。
(回忆我们的生成器函数就可以满足这两个条件)所以就引入了协程。
四、协程的优点:
1、无需线程上下文切换的开销
2、无需原子操作锁定及同步的开销。(原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序是不可以被打乱,或者切割掉只执行部分。视作整体是原子性的核心。)
3、方便切换控制流,简化编程模型。高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理
五、协程的缺点:
1、无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。
2、进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序
六、补充部分
协程:协助程序,线程和进程都是抢占式特点,线程和进程的切换我们是不能参与的。而协程是非
抢占式特点,协程也存在着切换,这种切换是由我们用户来控制的。协程主解决的是IO的操作。协
程,又称微线程,
优点1: 协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线
程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。
优点2: 不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享
资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。
因为协程是一个线程执行,那怎么利用多核CPU呢?最简单的方法是多进程+协程,既充分利用多
核,又充分发挥协程的高效率,可获得极高的性能。
七、并发、并行、同步、异步、阻塞、非阻塞
并发:指一个时间段内,有几个程序在同一个cpu上运行,但是任意时刻只有一个程序在cpu上运行。比如说在一秒内cpu切换了100个进程,就可以认为cpu的并发是100。
并行:值任意时刻点上,有多个程序同时运行在cpu上,可以理解为多个cpu,每个cpu独立运行自己程序,互不干扰。并行数量和cpu数量是一致的。
我们平时常说的高并发而不是高并行,是因为cpu的数量是有限的,不可以增加
同步:(注意同步和异步只是针对于I/O操作来讲的)指调用IO操作时,必须等待IO操作完成后才开始新的的调用方式。
异步:指调用IO操作时,不必等待IO操作完成就开始新的的调用方式。
阻塞:指调用函数的时候,当前线程被挂起。
非阻塞:指调用函数的时候,当前线程不会被挂起,而是立即返回。
八、IO多路复用:
sllect, poll, epoll都是IO多路复用的机制。IO多路复用就是通过这样一种机制:一个进程可以监听
多个描述符,一旦某个描述符就绪(一般是读就绪和写就绪),能够通知程序进行相应的操作。但
select,poll,epoll本质上都是同步IO,因为他们都需要在读写事件就绪后自己负责进行读写(即将数
据从内核空间拷贝到应用缓存)。也就是说这个读写过程是阻塞的。而异步IO则无需自己负责读写,
异步IO的实现会负责把数据从内核拷贝到用户空间。
select函数监听的文件描述符分三类:writefds、readfds、和exceptfds。调用后select函数会阻塞,
直到描述符就绪(有数据可读、写、或者有except)或者超时(timeout指定等待时间,如果立即返
回则设置为null),函数返回。当select函数返回后,可以通过遍历fdset,来找到就绪的描述符。
优点:良好的跨平台性(几乎所有的平台都支持)
缺点:单个进程能够监听的文件描述符数量存在最大限制,在linux上一般为1024,可以通过修改宏定
义甚至重新编译内核来提升,但是这样也会造成效率降低。
poll不同于select使用三个位图来表示fdset的方式,poll使用的是pollfd的指针实现
pollfd结构包含了要监听的event和发生的event,不再使用select“参数-值”传递的方式。同时pollfd并
没有最大数量限制(但是数量过大之后性能也是会下降)。和select函数一样,poll返回后,需要轮
询pollfd来获取就绪的描述符。
从上面看,select和poll都需要在返回后,通过遍历文件描述符来获取已经就绪的socket。事实
上,同时连接的大量客户端在同一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量
的增长,其效率也会下降。
epoll是在linux2.6内核中提出的,(windows不支持),是之前的select和poll增强版。相对于
select和poll来说,epoll更加灵活,没有描述符的限制。epoll使用一个文件描述符管理多个描述符,
将用户关系的文件描述符的时间存放到内核的一个时间表中。这样在用户控件和内核控件的coppy
只需要一次。
如何选择?
①在并发高同时连接活跃度不是很高的请看下,epoll比select好(网站或web系统中,用户请
求一个页面后随时可能会关闭)
②并发性不高,同时连接很活跃,select比epoll好。(比如说游戏中数据一但连接了就会一直
活跃,不会中断)
省略章节:由于在用到select的时候需要嵌套多层回调函数,然后印发一系列的问题,如可读性差,
共享状态管理困难,出现异常排查复杂,于是引入协程,既操作简单,速度又快。
网友评论