zoukankan      html  css  js  c++  java
  • join函数——Gevent源码分析

    在使用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协程~

  • 相关阅读:
    Memcache 内存分配策略和性能(使用)状态检查
    C# 中字符串转换成日期
    Task及Mvc的异步控制器 使用探索
    MVC项目实践,在三层架构下实现SportsStore-01,EF Code First建模、DAL层等
    从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 二十三║Vue实战:Vuex 其实很简单
    从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 二十一║Vue实战:开发环境搭建【详细版】
    vue-router 快速入门
    Vue.js——60分钟快速入门
    五小步让VS Code支持AngularJS智能提示
    AngularJS----服务,表单,模块
  • 原文地址:https://www.cnblogs.com/eric-nirnava/p/geventjoin.html
Copyright © 2011-2022 走看看