Python学习第52天(协程)
关于协程,最早出现在处理io密集型和计算密集型问题中,出现过多进程加协程的处理模式,首先关于协程,其本质就是一个微线程。
先说一下之前我们学到的线程和进程,这两者有一个非常明显和共同的特点,都属于抢占式程序,之间的切换是完全不受我们控制的,执行切换过程是属于操作系统级别的操作,所以与之对应,协程就出现了,其实协程的本质就是在你控制下进行线程之间的切换,其实我们在之前的生成器部分有讲到一个yield,就有类似的效果。
先来回顾一下yield的使用方法吧
def f(): print('hello world 1') yield print('hello world 2') yield print('hello world 3') gen = f()
print(type(gen)) gen.__next__() gen.__next__() gen.__next__()
当含有yield的函数在最初运行的时候,预运行的时候会被系统识别,正式开始运行的时候,不是直接开始输出其中的语句,而是生成一个生成器,随后我们可以通过内置方法next()或者__next__()方法调用,每调用一次就会输出到yield处,随后保存运行的状态,待下一次运行next()的时候继续从上一次的next处向下运行,遇到下一个yield时保存状态,停止运行,同时yield也有return的功能,同样可以返回参数。
其实yield的功能是类似于我们的协程的,所以我们先试用yield实现以下协程的功能。
import time import queue def consumer(name): print("--->ready to eat baozi...") while True: new_baozi = yield print("[%s] is eating baozi %s" % (name,new_baozi)) #time.sleep(1) def producer(): r = con.__next__() r = con2.__next__() n = 0 while 1: time.sleep(1) print("\033[32;1m[producer]\033[0m is making baozi %s and %s" %(n,n+1) ) con.send(n) con2.send(n+1) n +=2 if __name__ == '__main__': con = consumer("c1") con2 = consumer("c2") p = producer()
虽然能够基本实现,但是我们在实际操作的过程总不能每切换一次就是用一次next吧,太麻烦了,所以我们引入了greenlet模块
greenlet是一个用C实现的协程模块,相比与python自带的yield,它可以使你在任意函数之间随意切换,而不需把这个函数先声明为generator
看一下实例吧。
from greenlet import greenlet def test1(): print(12) gr2.switch() print(34) gr2.switch() def test2(): print(56) gr1.switch() print(78) gr1 = greenlet(test1) gr2 = greenlet(test2) gr1.switch()
虽然比yield好了很多,可以实现任意函数之间的切换,但是每次切换都需要人为的操作,不好不好,所以我们也要引入不用自己切换的这种模式。
gevent模块
import gevent import requests,time start=time.time() def f(url): print('GET: %s' % url) resp =requests.get(url) data = resp.text print('%d bytes received from %s.' % (len(data), url)) gevent.joinall([ gevent.spawn(f, 'https://www.python.org/'), gevent.spawn(f, 'https://www.yahoo.com/'), gevent.spawn(f, 'https://www.baidu.com/'), gevent.spawn(f, 'https://www.sina.com.cn/'), ]) # f('https://www.python.org/') # # f('https://www.yahoo.com/') # # f('https://baidu.com/') # # f('https://www.sina.com.cn/') print("cost time:",time.time()-start)
大致就是这个样子的,这就有点类似多线程的时候了,所以我们可以综合使用嘛。
现在可以总结一下协程的优势了:
优点1: 协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。
优点2: 不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。
因为协程是一个线程执行,那怎么利用多核CPU呢?最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。
以上就是主要内容部分,其实前面的知识现在开始忘的连点印象都没有了,所以,复习复习,很重要。
最近工作也需要自己投入更多,加油加油。