一:gevent模块
import gevent def f(n): for i in range(n): print(gevent.getcurrent(), i) # 创建一个普通的greenlet对象,并进行切换 g1 = gevent.spawn(f, 5) g2 = gevent.spawn(f, 5) g3 = gevent.spawn(f, 5)
# 协程任务添加到事件循环 g1.join() g2.join() g3.join() <Greenlet at 0x27dae18: f(5)> 0 <Greenlet at 0x27dae18: f(5)> 1 <Greenlet at 0x27dae18: f(5)> 2 <Greenlet at 0x27dae18: f(5)> 3 <Greenlet at 0x27dae18: f(5)> 4 <Greenlet at 0x27daae8: f(5)> 0 <Greenlet at 0x27daae8: f(5)> 1 <Greenlet at 0x27daae8: f(5)> 2 <Greenlet at 0x27daae8: f(5)> 3 <Greenlet at 0x27daae8: f(5)> 4 <Greenlet at 0x27dabf8: f(5)> 0 <Greenlet at 0x27dabf8: f(5)> 1 <Greenlet at 0x27dabf8: f(5)> 2 <Greenlet at 0x27dabf8: f(5)> 3 <Greenlet at 0x27dabf8: f(5)> 4
从打印结果来说,并没有起到一个切换的效果,原因是没有遇到一个阻塞的条件
那么我们人为的制造阻塞,看看情况会是什么样子? 加上 gevent.sleep(1)
import gevent def f(n): for i in range(n): print(gevent.getcurrent(), i) gevent.sleep(1) # 创建一个普通的Greenlet对象并切换 g1 = gevent.spawn(f, 5) g2 = gevent.spawn(f, 5) g3 = gevent.spawn(f, 5) # 将协程任务添加到事件循环,接收一个任务列表 g1.join() g2.join() g3.join() <Greenlet at 0x29b9e18: f(5)> 0 <Greenlet at 0x29b9ae8: f(5)> 0 <Greenlet at 0x29b9bf8: f(5)> 0 <Greenlet at 0x29b9e18: f(5)> 1 <Greenlet at 0x29b9ae8: f(5)> 1 <Greenlet at 0x29b9bf8: f(5)> 1 <Greenlet at 0x29b9e18: f(5)> 2 <Greenlet at 0x29b9ae8: f(5)> 2 <Greenlet at 0x29b9bf8: f(5)> 2 <Greenlet at 0x29b9e18: f(5)> 3 <Greenlet at 0x29b9ae8: f(5)> 3 <Greenlet at 0x29b9bf8: f(5)> 3 <Greenlet at 0x29b9e18: f(5)> 4 <Greenlet at 0x29b9ae8: f(5)> 4 <Greenlet at 0x29b9bf8: f(5)> 4
gevent.sleep(1) 人为的制造了阻塞,其实内部还是调用的是 wait()方法
具体解释,参考https://www.cnblogs.com/lyx210019/p/9427146.html
sleep()方法必须传入参数,参数就是休眠时间,时间到了就会自动醒来。
wait()方法可以传入参数也可以不传入参数,传入参数就是在参数结束的时间后开始等待,不传参数就是直接等待。
内部是调用了这个方法,hub.wait(t)
但是这种形式的协程,需要人为的去控制阻塞,非常不方便,如果能够自己识别阻塞,自己来处理,就好了!
补丁monkey
import gevent import random import time def f(n,name): for i in range(n): print(name, i) time.sleep(random.random()) gevent.joinall([ gevent.spawn(f,5,"意大利"), gevent.spawn(f,5,"西班牙") ]) 意大利 0 意大利 1 意大利 2 意大利 3 意大利 4 西班牙 0 西班牙 1 西班牙 2 西班牙 3 西班牙 4
从结果来看:这种形式的 joinall 可以传入time模块的耗时,更加接近真实场景,但是无法实行切换,依然是串行的执行,那么怎么使用monkey进行自动检测阻塞?
from gevent import monkey monkey.patch_all() import gevent import random import time def f(n,name): for i in range(n): print(name, i) time.sleep(random.random()) gevent.joinall([ gevent.spawn(f,5,"意大利"), gevent.spawn(f,5,"西班牙") ]) # 结果1: 意大利 0 西班牙 0 西班牙 1 意大利 1 西班牙 2 意大利 2 西班牙 3 西班牙 4 意大利 3 意大利 4
# 结果2 意大利 0 西班牙 0 意大利 1 意大利 2 意大利 3 西班牙 1 意大利 4 西班牙 2 西班牙 3 西班牙 4
结果是time.sleep(random时间不一样),正常的场景也是,每个请求的阻塞时间不一样,因此也是能够实现切换,而且是自动切换,monkey.patch_all() 就替我们解决了
# 具体解释廖雪峰的讲解 https://www.liaoxuefeng.com/wiki/897692888725344/966405998508320
import gevent from gevent import monkey monkey.patch_all() import requests def test(url): print(url) response = requests.get(url) print("从网站返回的内容长度为"+str(len(response.content.decode("utf-8"))),url) def main(): work_list = [ gevent.spawn(test,"https://www.baidu.com"), gevent.spawn(test, "https://www.sohoo.com"), gevent.spawn(test, "https://www.sina.com") ] gevent.joinall(work_list) if __name__ == '__main__': main() # 结果 https://www.baidu.com https://www.sohoo.com https://www.sina.com 从网站返回的内容长度为505551 https://www.sina.com 从网站返回的内容长度为2349 https://www.baidu.com 从网站返回的内容长度为2082 https://www.sohoo.com
从结果上来看,先打印的https://www.baidu.com,但是请求这个网址的时候,monkey检测到了阻塞,立即进行切换,打印了https://www.sohoo.com,也检测到了阻塞,然后切换到打印https://www.sina.com,但是请求新浪没有阻塞或者阻塞时间特别短,获得了数据,并打印。然后百度阻塞接触,打印百度,最后搜狐阻塞完成,打印搜狐。
# TODO