协程又叫做微线程,协程是一种用户态的轻量级的线程,协程拥有自己的的寄存器的上下文和栈,协程调度切换时,将寄存器上下文和栈保存
到其他地方,在切换回来后,恢复之前保存的寄存器的上下文关系,因此协程能保留上一次调用的状态,每次过程重入的时候,就相当于
进入上一次调用的状态
协程一定在单线程中,协程的切换是在线程中切换,和单个线程在cpu之间不停的切换是一样的
但是线程切换是cpu控制的,而协程的切换是用户控制的,操作系统根本无感知;
协程的切换比线程的切换速度要快,效率要高
协程的好处
1、无需线程上下文切换的开销
2、无需原子操作锁定及同步的开销,因为协程是串行的
3、方便切换控制流,简化编程模型
4、高并发,高扩展,低成本,一个cpu支持上万个协程没有问题,所以非常适合高并发处理
协程的缺点
1、无法利用多核的优势,但是协程和进程配合就可以使协程运行在不同的cpu上
2、只要一个协程阻塞(Blocking),就会阻塞整个协程,因为协程是串行的,这个问题必须要解决,才能让协程大范围应用
解决方法:
1、如果遇到io操作,则进行协程切换,可以用gevent来实现
我们来看一段代码
#Gevent主要是解决协程阻塞的问题,Gevent是一个第三方库,可以轻松通过Gevent实现并发同步 # 或者异步编程,在gevent中用到的主要模式是Greenlet,他是以C扩展模块形式接入Python的轻量级 # 协程,Greenlet全部运行在主程序操作系统进程的内部,但是被协作式调度
import gevent import time def foo(): print("/033[41;1mrunning in foo/033[0m") gevent.sleep(1) #触发协程切换 # time.sleep(1) print("/033[41;1mExplicit context switch to foo aga/033[0m") def Bar(): print("/033[42;1mExplicit context to bar/033[0m") gevent.sleep(2) #触发协程切换 # time.sleep(2) print("/033[42;1mImplicit context switch back to bar/033[0m") def Car(): print("/033[43;1mExplicit context to bar/033[0m") gevent.sleep(3) #触发协程切换 # time.sleep(3) print("/033[43;1mImplicit context switch back to bar/033[0m") gevent.joinall( [gevent.spawn(foo), gevent.spawn(Bar), gevent.spawn(Car) ] ) # 1、running in foo # 2、Explicit context to bar # 3、Explicit context to bar # 4、Explicit context switch to foo aga # 5、Implicit context switch back to bar # 6、Implicit context switch back to bar #先同时打印1,然后遇到gevent,就会进行协程切换,切换到协程2,打印2,然后遇到gevent,又会进行协程切换,打印3,又遇到gevent,再次进行协程切换 #切换到协程1,这个时候就算
使用urllib+gevent实现多协程的网页爬虫
from gevent import monkey; monkey.patch_all() import gevent import urllib.request #可以简单的爬网页的一个模块 def f(url): print("GET:[%s]" %(url)) resp = urllib.request.urlopen(url) data = resp.read() print("[%d] bytes received from %s" %(len(data),url)) gevent.joinall( [gevent.spawn(f,"http://www.baidu.com/"), gevent.spawn(f,"http://www.python.org/"), gevent.spawn(f,"http://www.sina.com/"), gevent.spawn(f,"http://www.163.com/"), ] )
结果如下,遇到阻塞就会自动跳转到其他的协程,这个阻塞gevent会自动判断的
GET:[http://www.baidu.com/] GET:[http://www.python.org/] GET:[http://www.sina.com/] GET:[http://www.163.com/] [111488] bytes received from http://www.baidu.com/ [601132] bytes received from http://www.sina.com/ [660092] bytes received from http://www.163.com/ [48713] bytes received from http://www.python.org/
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/20873.html