在使用gevent框架的时候,我们经常会使用join函数,如下:

def test1(id):
print(id)
gevent.sleep(0)
print(id, 'is done!') t = gevent.spawn(test1, 't')
t1 = gevent.spawn(test1, 't1')
t.join()

运行结果:

t
t1
t is done!
t1 is done!

但是join是如何工作的呢.. 于是今天晚上我好好研究了下join函数~ 多的不说,正文开始!

join函数

join函数源码在greenlet.py中的Greenlet类的join():

def join(self, timeout=None):
if self.ready(): #检测是否执行完成
return
else:
switch = getcurrent().switch #获得当前greenlet的switch回调函数
self.rawlink(switch)
try:
t = Timeout.start_new(timeout)
try:
result = self.parent.switch()
assert result is self, '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

从join的源码第六行,跟踪到rawlink函数:

def rawlink(self, callback):
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的回调函数(第四行), 当主协程hub还没有run的时候,这个时候的greenlet可以理解为一个上下文(这块涉及到greenlet的底层,还不是很清楚)。

回到join函数。在注册了当前greenlet的回调函数后,主要干的事是第10行:切换到主协程hub

主协程的switch函数的功能我在前面的文章中写过了,不再赘述。它会执行greenlet.switch(self),由于当前协程为hub,并且没有运行过run,所以会执行hub.run函数,源码在hub.py下的Hub类里面(这个函数也在前面的文章中讲过,所以不再详细说明)。在这个函数里面就会执行gevent的一般流程(前面的文章讲过)

如果你已经理解了join函数和gevent的工作原理,那么就可以解释以下函数的输出:

def test1(id):
print(id)
gevent.sleep(0)
print(id, 'is done!') t = gevent.spawn(test1, 't')
gevent.sleep(0)

输出(为什么没有继续输出t is done!?):

t

答案:

  1. 创建子协程t
  2. 执行到sleep函数,由于此时主协程hub还没有运行hub.run(),sleep函数中,语句loop.run_callback(waiter.switch)保存的是当前协程(可以理解为上下文)的回调函数
  3. 调用waiter.get()函数
  4. waiter.get()函数调用hub.switch(),切换到主协程hub
  5. 由于主协程没有run,所以执行hub.run()函数
  6. 执行loop.run(),切换到子协程t中
  7. 执行_run()函数,即子协程的任务:我们定义的test1函数
  8. 当执行完test1中的sleep(0)的时候,会回到主协程hub,hub会执行之前保存的回调函数,即回到了上下文,不会再回到主协程hub,所以不会输出t is done!

同理,可以分析这个函数的输出:

def test1(id):
print(id)
gevent.sleep(0)
print(id, 'is done!') t = gevent.spawn(test1, 't')
gevent.sleep(0)
t.join()

输出:

t
t is done!

还有这个函数:

def test1(id):
print(id)
gevent.sleep(0)
print(id, 'is done!') t = gevent.spawn(test1, 't')
t1 = gevent.spawn(test1, 't1')
t1.join()
t2 = gevent.spawn(test1, 't2')

输出:

t
t1
t is done!
t1 is done!

提示:注意前文分析的“上下文”这个greenlet协程~

join函数——Gevent源码分析的更多相关文章

  1. sleep函数——Gevent源码分析

    gevent是一个异步I/O框架,当遇到I/O操作的时候,会自动切换任务,从而能异步地完成I/O操作 但是在测试的情况下,可以使用sleep函数来让gevent进行任务切换.示例如下: import ...

  2. switch函数——Gevent源码分析

    在gevent的源码中,经常能看到switch函数.而不同的类中的switch函数有不同的用法 1. greenlet的switch函数 这里面的greenlet是greenlet库中的greenle ...

  3. 转:[gevent源码分析] 深度分析gevent运行流程

    [gevent源码分析] 深度分析gevent运行流程 http://blog.csdn.net/yueguanghaidao/article/details/24281751 一直对gevent运行 ...

  4. 【Android笔记】Thread类中关于join()方法的源码分析

    1.join()方法的作用: 例如有一个线程对象为Thread1,在main()方法中调用Thread1.join()方法可使得当前线程(即主线程)阻塞,而执行Thread1线程. 2.源码分析(以上 ...

  5. Java字符串分割函数split源码分析

    spilt方法作用 以所有匹配regex的子串为分隔符,将input划分为多个子串. 例如: The input "boo:and:foo", for example, yield ...

  6. 内核堆分配函数brk()源码分析

    Evernote公开链接:http://www.evernote.com/shard/s133/sh/5b8d3b26-0e53-4c61-aa43-66f6e87bbcb7/a44096dd557f ...

  7. jQuery源码分析系列

    声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...

  8. jquery2源码分析系列

    学习jquery的源码对于提高前端的能力很有帮助,下面的系列是我在网上看到的对jquery2的源码的分析.等有时间了好好研究下.我们知道jquery2开始就不支持IE6-8了,从jquery2的源码中 ...

  9. [转]jQuery源码分析系列

    文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaro ...

随机推荐

  1. web中的中文字体的英文名称

    自从font-face出现以后,字体样式就不再是web开发者的难题了,但是对于移动端的中文来说,问题还是存在的,因为中文文件大小最少要3M+,即使选择性的加载某个字的字体,那也会出现不易替换的问题,所 ...

  2. 磁盘管理三-raid

    前言:何为raid raid是利用多个磁盘组成一个可提升效能.可包含冗余的磁盘阵列组.常用于数据吞吐量大(视频),冗余要求高的场景 当前raid包含了raid0-7,以及组合方式raid10,raid ...

  3. HDU 4970 Killing Monsters

    开始以为是线段树,算了一下复杂度也觉得能过...但是这题貌似卡了线段树... 具体做法: 对每一个塔,记录attack[l]+=d,attack[r+1]-=d;这样对于每个block,受到的伤害就是 ...

  4. Eclipse中设置条件断点

    1.在要添加断点的变量那一行前双击,添加断点: 2.在该断点处点击鼠标右键,在弹出的选项卡中选择“断点属性”Breakpoint Properties; 3.在断点属性选项卡中勾选Enabled复选框 ...

  5. Android 圆形按钮实现

    项目中用到的圆形按钮,做个半天,用sharp形式实现,样式代码如下: <Button android:id="@+id/btn_5" android:layout_width ...

  6. Spring整合Quartz

    目录[-] 一.Spring创建JobDetail的两种方式 二.整合方式一示例步骤 1.将spring核心jar包.quartz.jar和Spring-context-support.jar导入类路 ...

  7. JS~delegate与live

    在jquery里有两个方法可以用来绑定自动追加出来的DOM对象,它们是live和delegate,事实上,这两个方法是bind方法的一个变体,在对于固定DOM对象时,我们通常使用bind就可以了,而对 ...

  8. linux虚拟主机管理系统wdcp系列教程之三

    我们安装了网站服务管理系统wdcp之后,在使用过程中可能会出现这样或那样的疑问,下面给大家整理几点出来,方便大家学习.还有不懂的可以到wdlinux论坛寻找相关教程. 1.wdcp后台访问安全设置即限 ...

  9. [置顶] 软件设计之道_读书纪要.doc

    本系列的文档都是我读书后的个人纪要,如想了解更多相关内容,请购买正版物.对应的图书可以从我的个人图书列表里找寻:个人毕业后图书列表 1.  每个写代码的人都是设计师,团队里每个人都有责任保证自己的代码 ...

  10. Android应用程序窗口(Activity)的绘图表面(Surface)的创建过程分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/8303098 在前文中,我们分析了应用程序窗口连 ...