13.协程

协程:又称微线程,纤程,是一种用户态的轻量级线程,能够在单线程下跑出并发的状态

协程拥有自己的寄存器上下文和栈,协程调度切换时,将寄存器上下文和栈保存在其他地方,在切回来时,恢复先前保存的寄存器上下文和栈

因此:协程能够保存上一次调用时的状态,每次过程重入时,相当于进入上一次调用的状态

协程的优点和缺点:

优点:

无需线程上下文切换的开销
方便切控制流
无需原子操作锁定即同步的开销
高并发+高扩展+低成本:一个cpu支持上万的协程都不是问题

缺点:

无法利用多核cpu资源 解决方法;协程+多进程
使用yield可以实现简单的底层协程,通过yield来停止一个进程切换到另一个进程

实现协程:

pyhton提供了gevent模块来实现协程,需要提前安装

switch  协程之间的转换靠switch执行,如果再跳转回来,直接执行该switch的下一句代码

gevent.sleep() 模拟系统发生IO阻塞
gevent.joinall() 激活程序

代码如下:

from greenlet import greenlet

def test1():
print(12)
gr2.switch() # 跳转到test2()函数执行,如果再跳转回来,直接执行该句的下一句
print(34)
gr2.switch()

def test2():
print(56)
gr1.switch()
print(78)
gr1 = greenlet(test1)
gr2 = greenlet(test2)

gr1.switch()

#这类语句的作用是跳转到某个函数的语句中执行,如本是跳转执行test()函数

执行结果:

12
56
34
78

使用协程来写爬虫:

import gevent
from gevent import monkey   #补丁
import requests
import time
headers={
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36'
}
def get_html(url):
    print('get:%s'%url)
    resp=requests.get(url,headers=headers)
    test=resp.content
    print('%d bytes recevied from %s'%(len(test),url))
monkey.patch_all()    #在协程程序中,一旦cpu进入IO阻塞状态,cpu的线程就会切换到其他程序,因此要时刻监听cpu,在windows系统中,对cpu的监听不够严谨,加入此代码可以最大程度的监听cpu是否进入io阻塞的状态

print(time.ctime())  #开始事件
gevent.joinall([ #激活协程程序
gevent.spawn(get_html,'https://www.python.org/'),
gevent.spawn(get_html,'https://ww.yahoo.com/'),
gevent.spawn(get_html,'https://github.com'),
])
print(time.ctime())  #结束事件

 

 使用joinall和spawn可以确保cpu一旦进入阻塞状态,就会切换带其他代码中,可以大幅度的提高程序的执行速度

posted @ 2020-11-01 20:38  maday  阅读(95)  评论(0)    收藏  举报