生成器(generator):是一种⼀边循环⼀边计算的机制
是一种用普通函数语法定义的迭代器.生成器一定是迭代器(反之不成立)
创建生成器
第⼀种⽅法很简单,只要把⼀个列表⽣成式的[] 改成()
生成器推导式
|
|
yield
yield 保持简洁性的同时获得了 iterable 的效果.把一个函数改写为一个 generator 就获得了迭代能力
yield 是一个类似 return 的关键字,只是这个函数返回的是个生成器
yield 进程挂起 保持连接
使用生成器,定义斐波那契数列
|
|
在 for 循环执行时,每次循环都会执行 fab 函数内部的代码,执行到 yield b 时
fab 函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行
而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield
看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值.
|
|
手动调用 fab(5) 的 next() 方法(因为 fab(5) 是一个 generator 对象,该对象具有 next() 方法),
这样我们就可以更清楚地看到 fab 的执行流程
|
|
每次调用 fab 函数都会生成一个新的 generator 实例,各实例互不影响
多个yield
|
|
send()
|
|
⽣成器是这样⼀个函数,它记住上⼀次返回时在函数体中的位置.对⽣成器 函数的第⼆次(或第n次)调⽤跳转⾄该函数中间,⽽上次调⽤的所有局部变量都保持不变.
⽣成器不仅”记住”了它数据状态;⽣成器还”记住”了它在流控制构造(在命令 式编程中,这种构造不只是数据值)中的位置.
⽣成器的特点:节约内存. 迭代到下⼀次的调⽤时,所使⽤的参数都是第⼀次所保留下的,即是说,在整个所有函数调⽤的参数都是第⼀次所调⽤时保留的,⽽不是新创建的
yield控制进程的暂停与send又恢复
|
|
yiled多任务(协程,进程,线程)
协程:单进程单线程的多任务执行
yield ⼈⼯自动切换
而协程是由程序员自己决定怎样切换执行的,并且只能有一个执行路径
计算密集型需要占用大量的CPU资源,使用多进程
IO密集型需要网络功能,大量的时间都在等待网络数据的到来,使用多线程或线程
只切换了函数的调用,只要求保存函数的变量,而不像进程和线程的切换时,需要的其它内容的上下文
进程和线程的调用是由操作系统决定切换而执行的
gevent自动切换执行
|
|
当⼀个greenlet遇到IO(指的是input output 输⼊输出,⽐如⽹络、⽂件操作等)操作时,⽐如访问⽹络,就⾃动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执⾏
由于IO操作⾮常耗时,经常使程序处于等待状态,有了gevent为我们⾃动切换协程,就保证总有greenlet在运⾏,⽽不是等待IO
协程(又称微线程(coroutine),相当于单线程的能力)与子程序(函数调用是通过栈实现的,一个线程就是执行一个子程序,子程序就是协程的一种特例)
子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B,B在执行过程中又调用了C,C执行完毕返回,B执行完毕返回,最后是A执行完毕.
协程(协作完成任务),tornado好像就是用协程来实现异步的协程,协程看上去也是子程序,但执行过程中,在子程序内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行.
协程的特点在于是一个线程执行,所有协程有如下优势
最大的优势就是协程极高的执行效率.因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显.
第二大优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多.
整个流程无锁,由一个线程执行,produce和consumer协作完成任务,所以称为”协程”,而非线程的抢占式多任务. 子程序就是协程的一种特例.
线程确实比协程性能更好.因为线程能利用多核达到真正的并行计算,如果任务设计的好,线程能几乎成倍的提高你的计算能力,说线程性能不好的很多 是因为没有设计好导致大量的锁,切换,等待,这些很多都是应用层的问题.
而协程因为是非抢占式,所以需要用户自己释放使用权来切换到其它协程,因此 同一时间其实只有一个协程拥有运行权,相当于单线程的能力