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

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

    import gevent
    
    def test(id):
        print('Test %s is running...' % id)
        gevent.sleep(0)
        print('Test %s is done!' % id)
    
    gevent.joinall([gevent.spawn(test, i) for i in range(2)])
    

    该函数的执行结果是:

    Test 0 is running...
    Test 1 is running...
    Test 0 is done!
    Test 1 is done!
    

    可见,sleep函数能让gevent切换协程,进行异步操作。
    这次我想探究一下sleep函数的原理。

    在了解sleep函数之前,我们需要了解一下gevent的运行
    在前面的文章中,我们知道了gevent有个主协程hub的概念,当需要切换协程的时候,需要先回到hub,然后再由hub去切换。
    其实主协程hub是一个特殊的协程Greenlet
    当gevent运行的时候,gevent需要先创建一个主协程hub,并运行hub的run函数(具体源码在hub.py/run),比较简单,核心代码是loop.run(),这个run函数是Greenlet类中的run函数,用来切入loop中的子协程,源码在greenlet.py/run中。核心就是result = self._run(*self.args, **self.kwargs) , _run函数用来执行这个子协程的任务

    sleep函数

    在刚刚的示例代码中,在sleep处设置断点,进行跟踪。
    首先,进入sleep函数,函数在hub.py中:

    def sleep(seconds=0, ref=True):
        hub = get_hub()	#获得主协程hub对象
        loop = hub.loop		#获得主循环
        if seconds <= 0:
            waiter = Waiter()
            loop.run_callback(waiter.switch)	#设置回调函数(即下次本协程执行的地点)
            waiter.get()
        else:
            hub.wait(loop.timer(seconds, ref=ref))
    

    当seconds=0的时候,loop.run_callback(waiter.switch)把当前greenlet的switch注册到loop中,设置为回调函数,此时的loop是主协程hub下的loop。

    sleep函数中最后调用了waiter.get()get函数简化如下:

    def get(self):
    	assert self.greenlet is None, 'This Waiter is already used by %r' % (self.greenlet, )
    	self.greenlet = getcurrent()
    	try:
    		return self.hub.switch()
    	finally:
    		self.greenlet = None
    

    self.greenlet = getcurrent(): 把greenlet设置为当前协程greenlet
    return self.hub.switch(): 切换到主线程hub的主循环, 然后主循环再切换到下一个greenlet协程
    工作流程如图:

    总结

    Gevent的工作原理(省略了执行完协程之后的过程)如下:

    1. 程序启动,需要创建主协程hub
    2. 主协程执行hub.run()函数,里面主要是执行loop.run(),loop中是子协程,相当于执行子协程的run()函数
    3. 切换到子协程,执行Greenlet.run()函数
    4. Greenlet.run()函数中,执行到self._run()函数,即执行该协程的任务,本例中为自己定义的test()函数
    5. 一直执行到sleep(0)语句
    6. 在sleep()函数中保存回调的位置(即保存该协程执行到的地方),调用waiter.get()函数
    7. waiter.get()函数将调用self.hub.switch()切回主协程hub
    8. hub.switch()将调用greenlet.switch()函数:
      1. 如果即将切换的协程未执行过run函数,则执行run函数;
      2. 如果执行过run函数,则调用Waiter.switch()函数接着上次执行的地方执行

    重复以上的过程,直至所有协程任务全部执行完毕

  • 相关阅读:
    Could A New Linux Base For Tablets/Smartphones Succeed In 2017?
    使用libhybris,glibc和bionic共存时的TLS冲突的问题
    6 Open Source Mobile OS Alternatives To Android in 2018
    Using MultiROM
    GPU drivers are written by the GPU IP vendors and they only provide Android drivers
    Jolla Brings Wayland Atop Android GPU Drivers
    How to Use Libhybris and Android GPU Libraries with Mer (Linux) on the Cubieboard
    闲聊Libhybris
    【ARM-Linux开发】wayland和weston的介绍
    Wayland and X.org problem : Why not following the Android Solution ?
  • 原文地址:https://www.cnblogs.com/eric-nirnava/p/geventsleep.html
Copyright © 2011-2022 走看看