章
目
录
gevent库作为Python并发编程一个强大的工具,能帮助我们实现并发同步或异步编程。今天,咱们就深入了解一下gevent库的具体用法。
一、gevent库简介
gevent库的核心模式是Greenlet,它是一种以C扩展模块形式接入Python的轻量级协程。简单来说,Greenlet就像是一个个小任务,它们都在主程序所在的操作系统进程中运行,不过这些小任务之间是协作调度的,不会互相抢占资源,这样就能让程序在处理多个任务时更加高效。
二、gevent库的安装
在使用gevent库之前,需要先进行安装。在命令行中执行以下命令即可完成安装:
pip install gevent
执行这个命令后,系统会自动下载并安装gevent库及其相关依赖。在安装过程中,你可能会看到类似下面这样的输出信息,显示正在收集和安装各个相关的包:
(emo is) C:\Users187\PycharmProjects\myproject>pip install gevent
collecting gevent
Using cached gevent-21.12.0-cp37-cp37m-win_amd64.whl(1.6 MB)
Collecting zope.event
Using cached zope.event-4.5.0-py2.py3-none-any.whl (6.8 kB)
Requirement already satisfied: setuptools in d: \programdata\anaconda3\envs\emotional_analysis\lib\site-packages (from gevent)(49.6.0.post20200814)
collecting greenlet<2.0,>=1.1.0;platform_python_implementation=="cpython"
Using cached greenlet-1.1.2-cp37-cp37m-win_amd64.whl (101 kB)
Requirement already satisfied: cffi>=1.12.2;platform_python_implementation=="cpython" and sys_platform=="win32" in d:\programdata\anaconda3\envs\e es (from gevent)(1.14.3)
Requirement already satisfied: zope.interface in d:\programdata\anaconda3\envs\emotional_analysis\lib\site-packages (from gevent)(5.4.0)
三、gevent库的使用示例
(一)基本使用示例
下面通过一个简单的示例,来看看gevent库的基本用法:
from gevent import monkey # 为了能识别time模块的io
monkey.patch_all() # 必须放到被打补丁者的前面,如 time,socket 模块之前
import gevent
import time
def gf(name):
print(f'{name}:我想打王者!!')
# gevent.sleep(2)
time.sleep(2)
print(f'{name}:我想吃大餐!!!')
def bf(name):
print(f'{name}:一起打!!!')
# gevent.sleep(2)
time.sleep(2)
print(f'{name}:一快去吃!!')
if __name__ == "__main__":
start = time.time()
# 创建协程对象
g1 = gevent.spawn(gf, '张三')
g2 = gevent.spawn(bf, '李四')
# 开启任务
g1.join()
g2.join()
end = time.time()
print(end - start)
在这段代码中:
monkey.patch_all()
的作用是对一些标准库进行补丁操作,让gevent能够识别time
模块这类标准库中的IO操作,实现协程的切换。这个操作需要放在导入其他可能被打补丁的模块(如time
、socket
等)之前。- 定义了两个函数
gf
和bf
,函数内部先打印一句话,然后通过time.sleep(2)
模拟一个耗时操作。这里如果使用gevent.sleep(2)
,效果会更明显,它会让当前协程暂停2秒,同时允许其他协程执行,但这里为了体现gevent对标准库time.sleep
的处理能力,使用了标准库的time.sleep
。 - 在
if __name__ == "__main__":
代码块中,创建了两个协程对象g1
和g2
,分别绑定gf
函数和bf
函数,并传入不同的参数。然后通过join
方法来等待这两个协程执行完毕,最后计算并打印整个过程所花费的时间。
运行这段代码,你会看到类似下面的输出结果:
张三:我想打王者!!
李四:一起打!!!
张三:我想吃大餐!!!
李四:一快去吃!!
2.0309953689575195
Process finished with exit code 0
从结果可以看出,两个协程是并发执行的,虽然每个函数内部都有2秒的等待时间,但总的执行时间大约是2秒,而不是4秒,这就体现了gevent库在并发处理上的优势。
(二)多协程切换示例
再来看一个更能体现协程切换的示例:
import gevent
def foo():
print('Running in foo')
gevent.sleep(2)
print('Explicit context switch to foo again')
def bar():
print('Explicit context to bar')
gevent.sleep(2)
print('Implicit context switch back to bar')
gevent.joinall([
gevent.spawn(foo),
gevent.spawn(bar),
])
在这个示例中:
- 定义了
foo
和bar
两个函数,函数内部都使用了gevent.sleep(2)
来模拟耗时操作。gevent.sleep
会使当前协程暂停执行,将执行权交给其他可运行的协程。 - 使用
gevent.joinall
方法来启动并等待foo
和bar
两个协程执行完毕。gevent.joinall
接受一个协程对象列表作为参数,它会阻塞当前线程,直到列表中的所有协程都执行完成。
运行这段代码,输出结果如下:
Running in foo
Explicit context to bar
Explicit context switch to foo again
Implicit context switch back to bar
Process finished with exit code 0
从输出结果可以清晰地看到,在foo
函数执行过程中,遇到gevent.sleep(2)
时,程序会切换到bar
函数执行,等bar
函数执行到gevent.sleep(2)
时,又会切换回foo
函数继续执行,这就是协程的上下文切换过程。
(三)基于gevent的Socket服务器示例(程序准确性待检查)
下面是一个使用gevent实现的简单Socket服务器示例:
from gevent import monkey, socket, pool
monkey.patch_all()
def server(port, pool):
s = socket.socket()
s.bind(('0.0.0.0', port))
s.listen()
while True:
cli, addr = s.accept()
print("Welcome %s to SocketServer" % str(addr[0]))
# 通过pool.spawn()运行协程
pool.spawn(handle_request, cli)
def handle_request(conn):
try:
data = conn.recv(1024)
print("recv:", data)
data = "From SockeServer:192.168.1.1---%s" % data.decode("utf-8")
conn.sendall(bytes(data, encoding='utf-8'))
if not data:
conn.shutdown(socket.SHUT_WR)
except Exception as e:
print(e)
finally:
conn.close()
if __name__ == '__main__':
# 限制并发协程数量为5
pool = pool.Pool(5)
server(80, pool)
在这个示例中:
- 同样使用
monkey.patch_all()
对相关模块进行补丁操作,确保gevent能够正常工作。 server
函数创建了一个Socket服务器,绑定到本地的80端口并开始监听。每当有新的客户端连接时,它会打印欢迎信息,并通过pool.spawn
方法启动一个新的协程来处理客户端请求,这里的pool
是一个协程池,限制了并发协程的数量为5。handle_request
函数负责处理客户端的具体请求,接收客户端发送的数据,对数据进行简单处理后再发送回客户端。如果接收不到数据,就关闭连接。- 在
if __name__ == '__main__':
代码块中,创建了一个最大容纳5个协程的协程池pool
,并启动了服务器。
这个示例展示了gevent在网络编程中的应用,通过协程实现了高效的并发处理,不过需要注意的是,原文标注该程序准确性待检查,实际使用时可能需要进一步调试和优化。
通过以上内容,相信你对gevent库的基本用法有了更深入的了解。在实际开发中,根据不同的需求合理运用gevent库,能够有效提升程序的性能和并发处理能力。