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

  1. def test1(id):
  2. print(id)
  3. gevent.sleep(0)
  4. print(id, 'is done!')
  5. t = gevent.spawn(test1, 't')
  6. t1 = gevent.spawn(test1, 't1')
  7. t.join()

运行结果:

  1. t
  2. t1
  3. t is done!
  4. t1 is done!

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

join函数

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

  1. def join(self, timeout=None):
  2. if self.ready(): #检测是否执行完成
  3. return
  4. else:
  5. switch = getcurrent().switch #获得当前greenlet的switch回调函数
  6. self.rawlink(switch)
  7. try:
  8. t = Timeout.start_new(timeout)
  9. try:
  10. result = self.parent.switch()
  11. assert result is self, 'Invalid switch into Greenlet.join(): %r' % (result, )
  12. finally:
  13. t.cancel()
  14. except Timeout as ex:
  15. self.unlink(switch)
  16. if ex is not t:
  17. raise
  18. except:
  19. self.unlink(switch)
  20. raise

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

  1. def rawlink(self, callback):
  2. if not callable(callback):
  3. raise TypeError('Expected callable: %r' % (callback, ))
  4. self._links.append(callback)
  5. if self.ready() and self._links and not self._notifier:
  6. 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的工作原理,那么就可以解释以下函数的输出:

  1. def test1(id):
  2. print(id)
  3. gevent.sleep(0)
  4. print(id, 'is done!')
  5. t = gevent.spawn(test1, 't')
  6. gevent.sleep(0)

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

  1. 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!

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

  1. def test1(id):
  2. print(id)
  3. gevent.sleep(0)
  4. print(id, 'is done!')
  5. t = gevent.spawn(test1, 't')
  6. gevent.sleep(0)
  7. t.join()

输出:

  1. t
  2. t is done!

还有这个函数:

  1. def test1(id):
  2. print(id)
  3. gevent.sleep(0)
  4. print(id, 'is done!')
  5. t = gevent.spawn(test1, 't')
  6. t1 = gevent.spawn(test1, 't1')
  7. t1.join()
  8. t2 = gevent.spawn(test1, 't2')

输出:

  1. t
  2. t1
  3. t is done!
  4. 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. linux创建SVN客户端,服务器

    1- linux基本都自带svn 2-创建svn服务器 新创建服务器代码仓库 # svnadmin create serversvn 这样,我们就在~/目录下新建了版本库serverSvn. 3-修改 ...

  2. django获取某一个字段的列表,values/values_list/flat

    class Building(models.Model): corporation = models.ForeignKey('company.Corporation', verbose_name=u' ...

  3. information_schema.key_column_usage 学习

    information_schema.key_column_usage 表可以查看索引列上的约束: 1.information_schema.key_column_usage 的常用列: 1.cons ...

  4. Git学习05 --分支管理02

    1.冲突 产生冲突后,查看readme.txt   可以看到冲突内容 <<<<<<< ======= >>>>>>> ...

  5. Multi-Channel MAC for Ad Hoc Networks: Handling Multi-Channel Hidden Terminals Using A Single Transceiver

    MAC协议 2004 这是一个单纯的Multi-Channel Ad Hoc场景,多信道,但不是DSA.没有PU,只是多信道利用问题,相对传统Ad Hoc,要解决的就是1)信道访问(如何使用多个信道) ...

  6. USB封包格式

    1.起始(SOP)封包 根集线器会在每1 ms时,送出SOF封包.这介于2个SOF封包之间的时间,即称为帧(frame).SOF封包虽是属于令牌封包的一种,但却具有独自的PID形态名称SOF.通常目标 ...

  7. C 语言---漂亮的宏定义

    写好C 语言,漂亮的宏定义很重要,使用宏定义可以防止出错,提高可移植性,可读性,方便性等等.下面列举一些成熟软件中常用得宏定义. 1.防止一个头文件被重复包含 #ifndef COMDEF_H #de ...

  8. SQL Server 2008空间数据应用系列十二:Bing Maps中呈现GeoRSS订阅的空间数据

    原文:SQL Server 2008空间数据应用系列十二:Bing Maps中呈现GeoRSS订阅的空间数据 友情提示,您阅读本篇博文的先决条件如下: 1.本文示例基于Microsoft SQL Se ...

  9. java 下拉框级联及相关(转)

    ActionLintsner都实现此接口,其它监听器可以监听的事件都可以被它捕获 public interface ActionListener extends EventListenerThe li ...

  10. poj 2411 Mondriaan's Dream(状态压缩dp)

    Description Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, af ...