协程
协程,又称微线程,纤程。英文名Coroutine。
协程是啥
协程是python个中另外一种实现多任务的方式,只不过比线程更小占用更小执行单元(理解为需要的资源)。 为啥说它是一个执行单元,因为它自带CPU上下文。这样只要在合适的时机, 我们可以把一个协程 切换到另一个协程。 只要这个过程中保存或恢复 CPU上下文那么程序还是可以运行的。
通俗的理解:在一个线程中的某个函数,可以在任何地方保存当前函数的一些临时变量等信息,然后切换到另外一个函数中执行,注意不是通过调用函数的方式做到的,并且切换的次数以及什么时候再切换到原来的函数都由开发者自己确定
协程和线程差异
在实现多任务时, 线程切换从系统层面远不止保存和恢复 CPU上下文这么简单。 操作系统为了程序运行的高效性每个线程都有自己缓存Cache等等数据,操作系统还会帮你做这些数据的恢复操作。 所以线程的切换非常耗性能。但是协程的切换只是单纯的操作CPU的上下文,所以一秒钟切换个上百万次系统都抗的住。
简单实现协程
importtimedefwork1():whileTrue: print("----work1---")yieldtime.sleep(0.5)defwork2():whileTrue: print("----work2---")yieldtime.sleep(0.5)defmain():w1 = work1() w2 = work2()whileTrue: next(w1) next(w2)if__name__ =="__main__": main()
运行结果:
----work1---
----work2---
----work1---
----work2---
----work1---
----work2---
......
greenlet
为了更好使用协程来完成多任务,python中的greenlet模块对其封装,从而使得切换任务变的更加简单
安装方式
使用如下命令安装greenlet模块:
sudo pip3 install greenlet
#coding=utf-8fromgreenletimportgreenletimporttimedeftest1():whileTrue:print"---A--"gr2.switch() time.sleep(0.5)deftest2():whileTrue:print"---B--"gr1.switch() time.sleep(0.5)gr1 = greenlet(test1)gr2 = greenlet(test2)#切换到gr1中运行gr1.switch()
运行效果
---A--
---B--
---A--
---B--
......
gevent
greenlet已经实现了协程,但是这个还的人工切换,是不是觉得太麻烦了,不要捉急,python还有一个比greenlet更强大的并且能够自动切换任务的模块gevent
其原理是当一个greenlet遇到IO(指的是input output 输入输出,比如网络、文件操作等)操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。
由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO
安装
pip3 install gevent
1. gevent的使用
importgeventdeff(n):foriinrange(n): print(gevent.getcurrent(), i)g1 = gevent.spawn(f,5)g2 = gevent.spawn(f,5)g3 = gevent.spawn(f,5)g1.join()g2.join()g3.join()
运行结果
012340123401234
可以看到,3个greenlet是依次运行而不是交替运行
2. gevent切换执行
importgeventdeff(n):foriinrange(n): print(gevent.getcurrent(), i)#用来模拟一个耗时操作,注意不是time模块中的sleepgevent.sleep(1)g1 = gevent.spawn(f,5)g2 = gevent.spawn(f,5)g3 = gevent.spawn(f,5)g1.join()g2.join()g3.join()
运行结果
000111222333444
3. 给程序打补丁
fromgeventimportmonkeyimportgeventimportrandomimporttimedefcoroutine_work(coroutine_name):foriinrange(10): print(coroutine_name, i) time.sleep(random.random())gevent.joinall([ gevent.spawn(coroutine_work,"work1"), gevent.spawn(coroutine_work,"work2")])
运行结果
work10work11work12work13work14work15work16work17work18work19work20work21work22work23work24work25work26work27work28work29
fromgeventimportmonkeyimportgeventimportrandomimporttime# 有耗时操作时需要monkey.patch_all()# 将程序中用到的耗时操作的代码,换为gevent中自己实现的模块defcoroutine_work(coroutine_name):foriinrange(10): print(coroutine_name, i) time.sleep(random.random())gevent.joinall([ gevent.spawn(coroutine_work,"work1"), gevent.spawn(coroutine_work,"work2")])
运行结果
work10work20work11work12work13work21work14work22work15work23work16work17work18work24work25work19work26work27work28work29
进程、线程、协程对比
简单总结
进程是资源分配的单位
线程是操作系统调度的单位
进程切换需要的资源很最大,效率很低
线程切换需要的资源一般,效率一般(当然了在不考虑GIL的情况下)
协程切换任务资源很小,效率高
多进程、多线程根据cpu核数不一样可能是并行的,但是协程是在一个线程中 所以是并发