协程是python个中另外一种实现多任务的方式,只不过比线程更小占用更小执行单元(理解为需要的资源)。 为啥说它是一个执行单元,因为它自带CPU上下文。这样只要在合适的时机, 我们可以把一个协程 切换到另一个协程。 只要这个过程中保存或恢复 CPU上下文那么程序还是可以运行的。
通俗的理解:在一个线程中的某个函数,可以在任何地方保存当前函数的一些临时变量等信息,然后切换到另外一个函数中执行,注意不是通过调用函数的方式做到的,并且切换的次数以及什么时候再切换到原来的函数都由开发者自己确定
通过yield实现基础协程
yield的特点就是能够执行方法函数时,暂停执行,然后再继续往下执行,这样就能够实现方法的切换执行
import time def dancing(): for i in range(5): print('正在跳舞。。。。%d' % i) yield time.sleep(0.1) def singing(): for i in range(5): print('正在唱歌。。。。%d' % i) yield time.sleep(0.1) def main(): dance = dancing() sing = singing() for i in range(5): next(dance) next(sing) time.sleep(0.1) if __name__ == '__main__': main()
运行结果为:
通过greenlet实现协程
python中的greenlet模块对yield进行封装,从而使得切换任务变的更加简单,使用前先安装:pip install greenlet
greenlet中先创建对象,然后调用switch()方法开始执行程序
import time from greenlet import greenlet def dancing(): for i in range(10): print('正在跳舞。。。。。%d' % i) sing.switch() time.sleep(0.1) def singing(): for i in range(10): print('正在唱歌。。。。。%d' % i) dance.switch() time.sleep(0.1) # 创建全局greenlet对象 dance = greenlet(dancing) sing = greenlet(singing) def main(): # 调用执行dance print('开始执行dance') dance.switch() if __name__ == '__main__': main()
运行结果为:
通过gevent实现协程
greenlet已经实现了协程,但是需要手动通过调用switch进行切换,而gevent模块比greenlet更强大的并且能够自动切换任务。
其原理是当一个greenlet遇到IO(指的是input output 输入输出,比如网络、文件操作等)操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。
使用前先安装:pip install gevent
gevent的基础使用案例
1.普通的耗时操作写法需要转化为gevent模块中的耗时操作,如time.sleep转化为gevent.sleep
2.使用join()开始执行对象中对应的方法并等待方法执行完成
import gevent import time def dancing(n): for i in range(n): print('正在跳舞...%d' % i) # time.sleep(0.2) # 普通的耗时操作,如time.sleep、socket.recv()、socket.connect()等 # 这些代码需要手动转换为gevent模块中对应的方法,才能被gevent认为是耗时操作 # 当gevent遇到了认可的耗时操作后,就会自动切换通过gevent创建的对象 # 若没有遇到耗时操作,则按顺序执行gevent对象 gevent.sleep(0.5) def singing(n): for i in range(n): print('正在唱歌...%d' % i) # time.sleep(0.2) gevent.sleep(0.5) def main(): print('--------main开始-------') # 创建对象,这时并没有执行方法里面的代码 dance = gevent.spawn(dancing, 5) sing = gevent.spawn(singing, 6) # join()开始执行对象中对应的方法并等待方法执行完成 # 在执行方法的过程中,遇到耗时操作,则自动会切换执行gevent对象中的其他方法。 dance.join() sing.join() print('-------main结束--------') if __name__ == '__main__': main()
运行结果为:
gevent的常用简洁写法
1.导入gevent中的monkey,可以将程序中的普通耗时操作转换为gevent中认可的耗时操作
2.使用gevent.joinall让程序更加简洁
import gevent from gevent import monkey import time # 有耗时操作时,需要执行这句话 # 能将普通的耗时操作转换为gevent中的耗时操作,这样就不用手动把耗时操作全都替换成gevent中对应的耗时操作了 monkey.patch_all() def work(name, num): for i in range(num): print('%s正在执行。。。。%d' % (name, i)) time.sleep(0.2) def main(): dance = gevent.spawn(work, '小明', 5) sing = gevent.spawn(work, '小花', 6) gevent.joinall([dance, sing]) if __name__ == '__main__': main()
运行结果为: