Python中gevent模块协程使用
作者:Python测试开发 发布时间:2023-10-23 02:56:46
目录
背景
什么是协程?
什么是 gevent?
协程的例子
Q&A
Q:gevent 无法捕获的耗时
A:猴子补丁
实践
异步 requests 请求
gevent 的锁
Tip
背景
因为 Python 线程的性能问题,在 Python 中使用多线程运行代码经常不能达到预期的效果。而实际开发中我们经常有高并发的需求,这就要求我们的代码在跑的更快的同时需要单位时间内执行更多的有效逻辑、减少无用的等待。
什么是协程?
我们可以认为线程是轻量级的进程,所以你也可以理解协程是轻量级的线程。协程即在一个线程执行 A 函数时可以随时中断去执行 B 函数,可以自由切换。但这个过程并不是函数调用,现象和多线程一样,实际上是一个线程。其中 gevent 是 Python 协程的一个经典实现。
什么是 gevent?
gevent 是 Python 的一个并发框架,基于 greenlet 实现,使用了 epoll 事件监听机制以及诸多其他优化而变得高效。其基本思想就是一个 greenlet 就是一个协程,当 greenlet 遇到 IO 操作时,比如访问网络,就会自动切换到其他的 greenlet,等待 IO 完成再切换回来继续执行。gevent 可以帮我们自动实现这个协程切换的过程。
协程的例子
代码:
import gevent,time
def f1():
for i in range(5):
print('function:@@@f1 | NUM: @@@',i)
# 此处阻塞,gevent会帮我们切换到其他协程去↓
gevent.sleep(0)
def f2():
for i in range(5):
print('function:@@@f2 | NUM: @@@',i)
# 此处阻塞,gevent会帮我们切换到其他协程去↑
gevent.sleep(0)
# 创建两个协程对象,分别去执行两个函数
xc1=gevent.spawn(f1)
xc2=gevent.spawn(f2)
# 将协程们交给gevent去执行
gevent.joinall([xc1,xc2])
执行结果:
function:@@@f1 | NUM: @@@ 0
function:@@@f2 | NUM: @@@ 0
function:@@@f1 | NUM: @@@ 1
function:@@@f2 | NUM: @@@ 1
function:@@@f1 | NUM: @@@ 2
function:@@@f2 | NUM: @@@ 2
function:@@@f1 | NUM: @@@ 3
function:@@@f2 | NUM: @@@ 3
function:@@@f1 | NUM: @@@ 4
function:@@@f2 | NUM: @@@ 4
如上,当 gevent 帮我们执行两个协程的时候,首先 xc1 执行到 gevent.sleep(0)时发生阻塞,此时,gevent 帮我们将切换到xc2,xc2 执行到 gevent.sleep(0)时又发生了阻塞,此时,gevent 又帮我们将切换到 xc1 去执行。
Q&A
Q:gevent 无法捕获的耗时
代码:
import gevent,time
def f1():
for i in range(5):
print('function:@@@f1 | NUM: @@@',i)
# 注意这里
time.sleep(0.1)
def f2():
for i in range(5):
print('function:@@@f2 | NUM: @@@',i)
# 注意这里
time.sleep(0.1)
# 创建两个协程对象,分别去执行两个函数
xc1=gevent.spawn(f1)
xc2=gevent.spawn(f2)
# 将协程们交给gevent去执行
gevent.joinall([xc1,xc2])
执行结果:
function:@@@f1 | NUM: @@@ 0
function:@@@f1 | NUM: @@@ 1
function:@@@f1 | NUM: @@@ 2
function:@@@f1 | NUM: @@@ 3
function:@@@f1 | NUM: @@@ 4
function:@@@f2 | NUM: @@@ 0
function:@@@f2 | NUM: @@@ 1
function:@@@f2 | NUM: @@@ 2
function:@@@f2 | NUM: @@@ 3
function:@@@f2 | NUM: @@@ 4
如上,你会发现,time.sleep(0.1)耗费的时间,gevent 无法捕捉,导致代码是串行的,虽然我们创建了协程,但是并没有起到异步的作用。
怎么办呢?请看下面的解决方案。
A:猴子补丁
对于无法捕获的耗时,gevent 为我们提供了猴子补丁,当我们为我们的程序打了猴子补丁,那么当我们的程序遇到任何耗时的操作,gevent 都会帮我们去自动切换协程,从而实现异步高并发。
代码:
import gevent,time
from gevent import monkey;monkey.patch_all()
def f1():
for i in range(5):
print('function:@@@f1 | NUM: @@@',i)
# 注意这里
time.sleep(0.1)
def f2():
for i in range(5):
print('function:@@@f2 | NUM: @@@',i)
# 注意这里
time.sleep(0.1)
# 创建两个协程对象,分别去执行两个函数
xc1=gevent.spawn(f1)
xc2=gevent.spawn(f2)
# 将协程们交给gevent去执行
gevent.joinall([xc1,xc2])
执行结果:
function:@@@f1 | NUM: @@@ 0
function:@@@f2 | NUM: @@@ 0
function:@@@f1 | NUM: @@@ 1
function:@@@f2 | NUM: @@@ 1
function:@@@f1 | NUM: @@@ 2
function:@@@f2 | NUM: @@@ 2
function:@@@f1 | NUM: @@@ 3
function:@@@f2 | NUM: @@@ 3
function:@@@f1 | NUM: @@@ 4
function:@@@f2 | NUM: @@@ 4
如上,你会发现协程的切换已经实现,问题完美解决。
实践
异步 requests 请求
代码:
from gevent import monkey;monkey.patch_all()
import gevent,time,requests
from urllib3 import disable_warnings
disable_warnings()
def req(url):
res = requests.get(url,verify=False)
if res:
print('URL:{} | CODE:{}!'.format(url,res.status_code))
else:
print('URL:{} FAILED!')
xc1=gevent.spawn(req,'https://www.baidu.com')
xc2=gevent.spawn(req,'https://www.gitee.com')
xc3=gevent.spawn(req,'https://www.huaweicloud.com')
gevent.joinall([xc1,xc2,xc3])
执行结果:
URL:https://www.baidu.com | CODE:200!
URL:https://www.huaweicloud.com | CODE:200!
URL:https://www.gitee.com | CODE:200!
gevent 的锁
代码:
from gevent import monkey;monkey.patch_all()
from gevent.lock import Semaphore
import gevent,time
# 信号量设置为1
s1=Semaphore(1)
def f1():
for i in range(5):
# 信号量-1,即拿到锁
s1.acquire()
print('function:@@@f1 | NUM: @@@',i)
# 信号量+1,即释放锁
s1.release()
# 猴子补丁帮忙识别阻塞
time.sleep(0.1)
def f2():
for i in range(5):
# 信号量-1,即拿到锁
s1.acquire()
print('function:@@@f2 | NUM: @@@',i)
# 信号量+1,即释放锁
s1.release()
# 猴子补丁帮忙识别阻塞
time.sleep(0.3)
# 创建两个协程对象,分别去执行两个函数
xc1=gevent.spawn(f1)
xc2=gevent.spawn(f2)
# 将协程们交给gevent去执行
gevent.joinall([xc1,xc2])
执行结果:
function:@@@f1 | NUM: @@@ 0
function:@@@f2 | NUM: @@@ 0
function:@@@f1 | NUM: @@@ 1
function:@@@f1 | NUM: @@@ 2
function:@@@f2 | NUM: @@@ 1
function:@@@f1 | NUM: @@@ 3
function:@@@f1 | NUM: @@@ 4
function:@@@f2 | NUM: @@@ 2
function:@@@f2 | NUM: @@@ 3
function:@@@f2 | NUM: @@@ 4
如上,可以看到,gevent 可以自动处理锁和阻塞。按阻塞规律,f1 和 f2 会交替执行,但是加上阻塞时间,因为 f2 的阻塞时间是 f1 的 3 倍,所以前 6 条打印中,f1 执行的次数是 f2 的三倍,即 gevent 可以自动判断和处理阻塞和锁同时存在的情况。
Tip
在 HTTP 的异步开发中,猴子补丁要在导入 gevent 之前打,否则会出现异常。
来源:https://juejin.cn/post/6980507688308310029


猜你喜欢
- 今天是我们来玩一个钉子。通过一个钉子来学习一个opencv中的一个函数,这个函数我网上也有搜过,不过遗憾的是,各路好手都是写的是有点不堪入目
- 详解Oracle在out参数中访问光标一 概念申明包结构包头:负责申明包体:负责实现 二 需求查询某个部门中所有员工的所有信息三
- 官方文档: http://openpyxl.readthedocs.io/en/default/OpenPyXL库 --单元格样式设置单元格
- 从本篇开始讲述如何用css实现网页的布局,即如何用css控制网页内各个元素的显示位置。如果你是一个初学者,很可能觉得做一个网页的第一步就是布
- Anaconda 是一个基于 Python 的数据处理和科学计算平台,它已经内置了许多非常有用的第三方库,装上Anaconda,就相当于把
- 构建网络ResNet由一系列堆叠的残差块组成,其主要作用是通过无限制地增加网络深度,从而使其更加强大。在建立ResNet模型之前,让我们先定
- 简介rpc:远程过程调用协议。简单的来说就是客户端可以很方便得远程调用服务端的接口程序,而不用管底层是如何实现的。XML-RPC的全称是XM
- 文章背景每隔几年,开放式Web应用程序安全项目就会对最关键的Web应用程序安全风险进行排名。自第一次报告以来,注入风险高居其位!在所有注入类
- 一:数据源介绍本篇文章介绍的是使用python实现对葵花8号卫星数据进行自动下载。葵花8号卫星是日本的一颗静止轨道气象卫星,覆盖范围为60S
- JS操作二进制很麻烦,而且一直没有一个好的无损压缩工具来实现纯文本的压缩。所以钻研了一段时间的gzip,后来发现还是仅用 LZ77 比较容易
- 本文实例讲述了Python模块的制作方法。分享给大家供大家参考,具体如下:1 目的利用setup.py将框架安装到python环境中,作为第
- 一个网站程序,在添加新闻时出现错误以下是错误提示: Microsoft OLE DB Provide
- 数据库隔离级别有四种,应用《高性能mysql》一书中的说明:然后说说修改事务隔离级别的方法:1.全局修改,修改mysql.ini配置文件,在
- 在使用Context.ResponseWriter中的Set/WriteHeader/Write这三个方法时,使用顺序必须如下所示,否则会出
- java开发者在定义类中的方法时,不会关心方法的定义相对于调用语句的位置。但是python中需要注意:函数必须先定义、后调用(函数调用函数例
- 一、实验名称建立聊天工具二、实验目的掌握Socket编程中流套接字的技术,实现多台电脑之间的聊天。三、实验内容和要求vii.掌握利用Sock
- 简介bisect 库是 Python 标准库中的一部分,它提供了二分查找的功能。二分查找是一种在有序列表中查找某一特定元素的搜索算法。它的时
- 1、查看死锁1)用dba用户执行以下语句select username,lockwait,status,machine,program fr
- 不知道在坛子里有多少朋友使用触发器,如果你已经对触发器很了解了,那么请跳过此文,如果你还没有使用过触发器的话,那就让我们来认识一下吧。相关阅
- 查看当前连接系统参数:SHOW VARIABLES LIKE '%char%'; mysql> show variab