python之协程

协程又叫做微线程,协程是一种用户态的轻量级的线程,协程拥有自己的的寄存器的上下文和栈,协程调度切换时,将寄存器上下文和栈保存
到其他地方,在切换回来后,恢复之前保存的寄存器的上下文关系,因此协程能保留上一次调用的状态,每次过程重入的时候,就相当于
进入上一次调用的状态

协程一定在单线程中,协程的切换是在线程中切换,和单个线程在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("33[41;1mrunning in foo33[0m") gevent.sleep(1) #触发协程切换 # time.sleep(1) print("33[41;1mExplicit context switch to foo aga33[0m") def Bar(): print("33[42;1mExplicit context to bar33[0m") gevent.sleep(2) #触发协程切换 # time.sleep(2) print("33[42;1mImplicit context switch back to bar33[0m") def Car(): print("33[43;1mExplicit context to bar33[0m") gevent.sleep(3) #触发协程切换 # time.sleep(3) print("33[43;1mImplicit context switch back to bar33[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/

  

原文地址:https://www.cnblogs.com/bainianminguo/p/7430017.html