Python gevent协程切换实现详解
作者:冷冰若水 发布时间:2023-01-15 17:50:18
一、背景
大家都知道gevent的机制是单线程+协程机制,当遇到可能会阻塞的操作时,就切换到可运行的协程中继续运行,以此来实现提交系统运行效率的目标,但是具体是怎么实现的呢?让我们直接从代码中看一下吧。
二、切换机制
让我们从socket的send、recv方法入手:
def recv(self, *args):
while 1:
try:
return self._sock.recv(*args)
except error as ex:
if ex.args[0] != EWOULDBLOCK or self.timeout == 0.0:
raise
# QQQ without clearing exc_info test__refcount.test_clean_exit fails
sys.exc_clear()
self._wait(self._read_event)
这里会开启一个死循环,在循环中调用self._sock.recv()方法,并捕获异常,当错误是EWOULDBLOCK时,则调用self._wait(self._read_event)方法,该方法其实是:_wait = _wait_on_socket,_wait_on_socket方法的定义在文件:_hub_primitives.py中,如下:
# Suitable to be bound as an instance method
def wait_on_socket(socket, watcher, timeout_exc=None):
if socket is None or watcher is None:
# test__hub TestCloseSocketWhilePolling, on Python 2; Python 3
# catches the EBADF differently.
raise ConcurrentObjectUseError("The socket has already been closed by another greenlet")
_primitive_wait(watcher, socket.timeout,
timeout_exc if timeout_exc is not None else _NONE,
socket.hub)
该方法其实是调用了函数:_primitive_wait(),其仍然在文件:_hub_primitives.py中定义,如下:
def _primitive_wait(watcher, timeout, timeout_exc, hub):
if watcher.callback is not None:
raise ConcurrentObjectUseError('This socket is already used by another greenlet: %r'
% (watcher.callback, ))
if hub is None:
hub = get_hub()
if timeout is None:
hub.wait(watcher)
return
timeout = Timeout._start_new_or_dummy(
timeout,
(timeout_exc
if timeout_exc is not _NONE or timeout is None
else _timeout_error('timed out')))
with timeout:
hub.wait(watcher)
这里其实是调用了hub.wait()函数,该函数的定义在文件_hub.py中,如下:
class WaitOperationsGreenlet(SwitchOutGreenletWithLoop): # pylint:disable=undefined-variable
def wait(self, watcher):
"""
Wait until the *watcher* (which must not be started) is ready.
The current greenlet will be unscheduled during this time.
"""
waiter = Waiter(self) # pylint:disable=undefined-variable
watcher.start(waiter.switch, waiter)
try:
result = waiter.get()
if result is not waiter:
raise InvalidSwitchError(
'Invalid switch into %s: got %r (expected %r; waiting on %r with %r)' % (
getcurrent(), # pylint:disable=undefined-variable
result,
waiter,
self,
watcher
)
)
finally:
watcher.stop()
watcher.stop()
该类WaitOperationsGreenlet是Hub的基类,其方法wait中的逻辑是:生成一个Waiter对象,并调用watcher.start(waiter.switch, waiter)方法,watcher是最开始recv方法中使用的self._read_event,watcher是gevent的底层事件框架libev中的概念;同时还有一个waiter对象,它类似与python中的future概念,该对象有一个switch()方法以及get()方法,当没有得到结果没有准备好时,调用waiter.get()方法回导致协程被挂起;get()函数的定义如下:
def get(self):
"""If a value/an exception is stored, return/raise it. Otherwise until switch() or throw() is called."""
if self._exception is not _NONE:
if self._exception is None:
return self.value
getcurrent().throw(*self._exception) # pylint:disable=undefined-variable
else:
if self.greenlet is not None:
raise ConcurrentObjectUseError('This Waiter is already used by %r' % (self.greenlet, ))
self.greenlet = getcurrent() # pylint:disable=undefined-variable
try:
return self.hub.switch()
finally:
self.greenlet = None
在get()中最关键的是self.hub.switch()函数,该函数将执行权转移到hub,并继续运行,至此已经分析完了当在worker协程中从网络获取数据遇到阻塞时,如何避免阻塞并切换到hub中的实现,至于何时再切换会worker协程,我们后续再继续分析。
总结
要记得gevent中一个重要的概念,协程切换不是调用而是执行权的转移,从可能会阻塞的协程切换到hub,并由hub在合适的时机切换到另一个可以继续运行的协程继续执行;gevent通过这种形式实现了提高io密集型应用吞吐率的目标。
来源:https://www.cnblogs.com/lit10050528/p/13549532.html
猜你喜欢
- 摘要:现代网站和web应用程序趋向于依赖客户端的大量的javascript来提供丰富的交互。特别是通过不刷新页面的异步请求来返回数据或从服务
- 安装通过 pip 安装 Beautiful Soup 模块:pip install beautifulsoup4 。 还可以使用 PyCha
- 一、步骤要从 JPEG 图像中获取经纬度信息,可以使用 Python 的 PIL(Python Imaging Library)库。以下是一
- 一、牛顿多项式拉格朗日多项式的公式不具备递推性,每个多项式需要单独构造。但很多时候我们需要从若干个逼近多项式选择一个。这个时候我们就需要一个
- 数据库是什么 在学习ACCESS之前,我们先了解一下什么是“数据库”。我们举个例子来说明这个问题:每个人都有很多亲戚和朋友,为了保持与他们的
- 本文为大家分享了python实现图书馆研习室自动预约的具体代码,供大家参考,具体内容如下简介现在好多学校为学生提供了非常良好的学习环境,通常
- 本文实例为大家分享了python实现QQ空间自动点赞的具体代码,供大家参考,具体内容如下项目github地址使用python实现qq空间自动
- 本文实例为大家分享了python3.5绘制随机漫步图的具体代码,供大家参考,具体内容如下代码中我们定义两个模型,一个是RandomWalk.
- 下面的代码是日期函数的一些简单运用,应该不用解释,生成当月的日历,当然你可以根据实际情况进行扩充!效果图:<%@LANGUAGE=&q
- 本文将讨论Python中下划线(_)字符的使用方法。我们将会看到,正如Python中的很多事情,下划线的不同用法大多数(并非所有)只是常用惯
- 这是我研究网页切片算法的一个汇总想法。之前我写过:一种面向搜索引擎的网页分块、切片的原理,实现和演示,随着工作的深入,逐渐碰到以
- 每个 batch 前清空梯度,否则会将不同 batch 的梯度累加在一块,导致模型参数错误。然后我们将输入和目标张量都移动到所需的设备上,并
- 主程序mainaddfunc.pyfrom flask import Flask, render_template, request, ur
- 一、前言本次安装tensorflow是基于Python的,安装Python的过程不做说明(既然决定按,Python肯定要先了解啊):本次教程
- 一、查询条件精确,针对有参数传入情况 二、SQL逻辑执行顺序 FROM-->JOIN-->WHERE-->GROUP--&
- 如下所示:# 返回一个列表中第二大的数def second(ln):max = 0s = {}for i in range(len(ln))
- iUI、jQTouch、WPTouch、PhoneGap、XUI、iWebkit、Rhodes、gwt-mobile…当我们已经开始惊叹 w
- Yahoo!的Exceptional Performance团队为改善Web性能带来最佳实践。他们为此进行了一系列的实验、开发了
- <!-- #include file="../conn.asp" --&
- 这篇文章主要介绍了用python写测试数据文件过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋