协程,又叫微线程。协程是一种用户态的轻量级线程。
协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。协程能保留上一次调用的时候的状态,每次过程重入时,相当于进入上一次调用的状态。换种说法,进入上一次离开时所处的逻辑流的位置。
优势:
无需线程上下文切换的开销
无需原子操作锁定及同步的开销(就是不用锁)
方便切换控制流,简化编程模型
高并发,高扩展,低成本,一个cpu支持上万协程也没问题
缺点:
无法利用多核资源,协程本质上是单线程,也不能使用单个CPU的多核
进行阻塞(Blocking)操作会阻塞掉整个程序
手动挡协程实例,需要提前安装greenlet模块:
# -*- coding:utf-8 -*- # Author:Brownyangyang 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() #第一步,先执行gr1
自动挡实例,需要安装gevent第三方库:
# -*- coding:utf-8 -*- # Author:Brownyangyang import gevent def foo(): print('Running in foo') gevent.sleep(2) print('Explicit context switch to foo again') def bar(): print('Explicit context to bar') gevent.sleep(1) print('Implicit context switch back to bar') def func3(): print("running func3") gevent.sleep(0) print("running func3 again") gevent.joinall([ gevent.spawn(foo), gevent.spawn(bar), gevent.spawn(func3) ]) ##启动三个协程
遇到gevent.sleep就切换
使用协程爬网页:
# -*- coding:utf-8 -*- # Author:Brownyangyang from urllib import request import gevent from gevent import monkey #gevent默认识别不了urllib和socket的IO操作,所以要导入monkey monkey.patch_all() #把当前程序的所有IO操作单独记上标记 def f(url): print('GET:%s' % url) resp = request.urlopen(url) data = resp.read() print("%d bytes received from %s"% (len(data),url)) f = open("url.html","wb") f.write(data) f.close() 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://github.com/'), ])