在进行gevent源码学习一分析之后,我还对两个比较核心的问题抱有疑问:

  1. gevent.Greenlet.join()以及他的list版本joinall()的原理和使用。

  2. 关于在使用monkey_patchall()之后隐式切换的问题。

下面我将继续通过分析源码及其行为来加以理解和掌握。

1. 关于gevent.Greenlet.join()(以下简称join)先来看一个例子:

import gevent

def xixihaha(msg):
print(msg)
gevent.sleep(0)
print msg g1 = gevent.spawn(xixihaha, 'xixi')
gevent.sleep(0)

先分析一波

1. 初始化一个Greenlet实例g1,将该Greenlet.switch注册到hub上。

2. 然后调用gevent.sleep(0)将当前greenlet保存下来放在Waiter()中并向hub注册该回调。这里再贴一次实现的代码跟着走一遍强调一下实现,这对一会儿理解join实现非常有帮助:

    hub = get_hub()
loop = hub.loop
if seconds <= 0:
waiter = Waiter()
loop.run_callback(waiter.switch)
waiter.get()
else:
hub.wait(loop.timer(seconds, ref=ref))

这里我们将second设置为0,所以走第一层判断,初始化一个Waiter()对象给waiter,随后注册waiter.switch方法到hub,调用waiter.get()去调用hub的switch()方法(注意这里的self.hub.switch()方法并没有切换这个概念。这只是Hub类中自己实现的一个switch方法而已):

    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
else:
getcurrent().throw(*self._exception)
else:
if self.greenlet is not None:
raise ConcurrentObjectUseError('This Waiter is already used by %r' % (self.greenlet, ))
self.greenlet = getcurrent()
try:
return self.hub.switch()
finally:
self.greenlet = None

然后hub.switch(self)会返回一个greenlet.switch(self)这里才是切换 然后他会调用自己的run方法(greenlet底层实现)。

    def switch(self):
switch_out = getattr(getcurrent(), 'switch_out', None)
if switch_out is not None:
switch_out()
return greenlet.switch(self)
    def run(self):
"""
Entry-point to running the loop. This method is called automatically
when the hub greenlet is scheduled; do not call it directly. :raises LoopExit: If the loop finishes running. This means
that there are no other scheduled greenlets, and no active
watchers or servers. In some situations, this indicates a
programming error.
"""
assert self is getcurrent(), 'Do not call Hub.run() directly'
while True:
loop = self.loop
loop.error_handler = self
try:
loop.run()
finally:
loop.error_handler = None # break the refcount cycle
self.parent.throw(LoopExit('This operation would block forever', self))
# this function must never return, as it will cause switch() in the parent greenlet
# to return an unexpected value
# It is still possible to kill this greenlet with throw. However, in that case
# switching to it is no longer safe, as switch will return immediatelly

这里开始就进入事件loop循环了,run()会调用到注册过来的回调。这里开始g1注册过来的回调就会被调用了。

之后的流程就是运行g1注册的回调,然后运行Waiter()注册的回调,然后回到外层最后结束掉。打印结果:

xixi

那还有一个msg没有打印呢!怎么就退出来了!!这不科学。所以这就是join可以办到的事情了,来看源码:

    def join(self, timeout=None):
"""Wait until the greenlet finishes or *timeout* expires.
Return ``None`` regardless.
"""
if self.ready():
return switch = getcurrent().switch
self.rawlink(switch)
try:
t = Timeout._start_new_or_dummy(timeout)
try:
result = self.parent.switch()
if result is not self:
raise InvalidSwitchError('Invalid switch into Greenlet.join(): %r' % (result, ))
finally:
t.cancel()
except Timeout as ex:
self.unlink(switch)
if ex is not t:
raise
except:
self.unlink(switch)
raise

将当前的greenlet.switch方法赋值给switch然后调用rawlink方法:

    def rawlink(self, callback):
"""Register a callable to be executed when the greenlet finishes execution. The *callback* will be called with this instance as an argument. .. caution:: The callable will be called in the HUB greenlet.
"""
if not callable(callback):
raise TypeError('Expected callable: %r' % (callback, ))
self._links.append(callback)
if self.ready() and self._links and not self._notifier:
self._notifier = self.parent.loop.run_callback(self._notify_links)

rawlink其实也没做什么,他将当前greenlet的switch存进了一个双端链表中,就是self._links.append(callback)这一句。保存了起来并没有像sleep那样像hub上注册回调,所以hub在回调链里是没有这家伙的。

然后还是跟上面一样的流程用self.parent.switch()回到hub中调用greenlet.switch(self)运行run函数进入loop循环。然后运行第一个注册进来的回调也就是运行xixihaha并打印第一个msg。这个时候调用gevent.sleep注册一个Waiter()事件到hub,然后依然会回来。然后再执行最后一个msg 因为整个回调链上只有他自己只能又切回来。当运行完之后我们来看下如何回到最外面main:

    def run(self):
try:
self.__cancel_start()
self._start_event = _start_completed_event try:
result = self._run(*self.args, **self.kwargs)
except:
self._report_error(sys.exc_info())
return
self._report_result(result)
finally:
self.__dict__.pop('_run', None)
self.__dict__.pop('args', None)
self.__dict__.pop('kwargs', None)

当xixihaha回调也结束之后也就是第二个msg也运行完了之后会返回调用他的那个Greenlet.run方法继续向下执行,然后会执行到self._report_result(result)

    def _report_result(self, result):
self._exc_info = (None, None, None)
self.value = result
if self._has_links() and not self._notifier:
self._notifier = self.parent.loop.run_callback(self._notify_links)

这里我们直接看判断这里,会去hub上注册self._notify_links。 self._notify_links方法是什么来看:

    def _notify_links(self):
while self._links:
link = self._links.popleft()
try:
link(self)
except:
self.parent.handle_error((link, self), *sys.exc_info())

self._links.popleft()会让你把前面赋值给self._links的回调吐出来赋值给link然后运行这个回调。结果当然是愉快的回到了main里面。

然后弹出乱七八糟的东西最后结束run。

总结:

可以看到join和joinall()类似的都是没有把自己注册到hub主循环之中,而是等所有的greenlet都运行完了之后,再调用自己回到原来注册过去的greenlet中,就不会在因为提前切到主函数main中导致整个过程提前结束。

2. 关于在使用monkey_patchall()之后隐式切换的问题:

这个我不准备再拿特别大篇幅来分析讲解了,大致说下我的理解,首先必须要知道一点就是gevent的底层是libev,这也是为什么他如此高效的原因。hub里面的loop下就封装了各种各样对应的libev事件。就拿gevent.sleep()来举例子,里面使用的self.parent.loop.timer就是注册timer事件,而网络请求的切换只是将时间到期事件变成了io读写事件。每当io读取事件,写事件发生的时候,就会触发对应的事件gevent就是通过这些事件的触发来决定自身什么时候该切换到哪里进行哪些事件的处理。理解了这个,也就明白了隐式切换真正的实现原理。然后再去看源码可能就没有那么一脸萌比的感觉了。

对gevent的源码分析到这里,目前也足够我使用了。下一篇关于gevent的文章将分析和实践一些高级应用和特性,毕竟我们学库都是拿来使用的。

python 协程库gevent学习--gevent源码学习(二)的更多相关文章

  1. python协程详解,gevent asyncio

    python协程详解,gevent asyncio 新建模板小书匠 #协程的概念 #模块操作协程 # gevent 扩展模块 # asyncio 内置模块 # 基础的语法 1.生成器实现切换 [1] ...

  2. 手牵手,从零学习Vue源码 系列二(变化侦测篇)

    系列文章: 手牵手,从零学习Vue源码 系列一(前言-目录篇) 手牵手,从零学习Vue源码 系列二(变化侦测篇) 陆续更新中... 预计八月中旬更新完毕. 1 概述 Vue最大的特点之一就是数据驱动视 ...

  3. python 协程库gevent学习--源码学习(一)

    总算还是要来梳理一下这几天深入研究之后学习到的东西了. 这几天一直在看以前跟jd对接的项目写的那个gevent代码.为了查错,基本上深入浅出了一次gevent几个重要部件的实现和其工作的原理. 这里用 ...

  4. python 协程库gevent学习--gevent数据结构及实战(三)

    gevent学习系列第三章,前面两章分析了大量常用几个函数的源码以及实现原理.这一章重点偏向实战了,按照官方给出的gevent学习指南,我将依次分析官方给出的7个数据结构.以及给出几个相应使用他们的例 ...

  5. python 协程库gevent学习--gevent数据结构及实战(四)

    一不留神已经到第四部分了,这一部分继续总结数据结构和常用的gevent类,废话不多说继续. 1.Timeout错误类 晚上在调试调用第三方接口的时候,发现有些接口耗时非常多,觉得应该有个超时接口来限制 ...

  6. 菜鸟学习Fabric源码学习 — 背书节点和链码容器交互

    Fabric 1.4 源码分析 背书节点和链码容器交互 本文档主要介绍背书节点和链码容器交互流程,在Endorser背书节点章节中,无论是deploy.upgrade或者调用链码,最后都会调用Chai ...

  7. 菜鸟学习Fabric源码学习 — kafka共识机制

    Fabric 1.4源码分析 kafka共识机制 本文档主要介绍kafka共识机制流程.在查看文档之前可以先阅览raft共识流程以及orderer服务启动流程. 1. kafka 简介 Kafka是最 ...

  8. 学习JDK源码(二):Integer

    最近没有好好保持学习的好习惯,该打. 天天忙,感觉都不知道在干嘛.真的厌倦了普通的Java代码,还是想学点新技术. 用了这么久的Java,最常用的数据类型肯定是Int了,而他的包装类Integer用的 ...

  9. 菜鸟学习Fabric源码学习 — Endorser背书节点

    Fabric 1.4 源码分析 Endorser背书节点 本文档主要介绍fabric背书节点的主要功能及其实现. 1. 简介 Endorser节点是peer节点所扮演的一种角色,在peer启动时会创建 ...

随机推荐

  1. zabbix学习小结

    一.zabbix是干什么的?    zabbix主要用来做监控.监控什么呢?比如日常巡检的CPU.内存.磁盘.swap交换分区和各端口进程等.    以往日常巡检,通过df -h命令获得磁盘的使用量和 ...

  2. 邻接表&链式前向星

    链式前向星: 适合点多.边少的情况 不适用于大量遍历出边的题目(因为cache miss) 邻接表: 如果用邻接表来实现的话,一般就用vector嘛,我们都知道vector都是自动扩容的,在空间满了以 ...

  3. [转]系统架构演变--集中式架构-垂直拆分-分布式服务-SOA(服务治理)-微服务

    一.系统架构演变 1.1. 集中式架构 当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本.此时,用于简化增删改查工作量的数据访问框架(ORM)是影响项目开发的关键. 存在的 ...

  4. Spring Security 重定向原理分析

    本文基于 spring-security-core-5.1.1 和 tomcat-embed-core-9.0.12. 一个用户访问使用表单认证的 Web 应用时,后端的处理流程大致如下: 1.用户访 ...

  5. CVE-2016-7912 分析报告

    CVE-2016-7912 背景介绍 在内核USB驱动中,进行异步读取或写入时,调用ki_complete(),会提前释放kiocb结构体,从而造成UAF漏洞,但经过分析,发现无法利用此漏洞进行攻击. ...

  6. PAT A1076 Forwards on Weibo (30 分)——图的bfs

    Weibo is known as the Chinese version of Twitter. One user on Weibo may have many followers, and may ...

  7. Got fatal error 1236 from master when reading data from binary log: 'Client requested master to start replication from impossible position

    在source那边,执行: flush logs;show master status; 记下File, Position. 在target端,执行: CHANGE MASTER TO MASTER_ ...

  8. [06] Bean属性的注入

    之前我们提到了Bean实例化的三种方式:构造器方式.静态工厂方式.普通工厂方式.那么对于Bean中的属性,又是如何进行注入的(依赖注入),这个篇章就来提一提. 1.先提提什么是"依赖注入&q ...

  9. CF809D Hitchhiking in the Baltic States LIS、平衡树

    传送门 看到最长上升子序列肯定是DP 设\(f_i\)表示计算到当前,长度为\(i\)的最长上升子序列的最后一项的最小值,显然\(f_i\)是一个单调递增的序列. 转移:对于当前计算的元素\(x\), ...

  10. python 3.5下安装pycrypto

    pip install --use-wheel --no-index --find-links=https://github.com/sfbahr/PyCrypto-Wheels/raw/master ...