gevent

一: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

原文地址:https://www.cnblogs.com/meloncodezhang/p/12510869.html