博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
sleep函数——Gevent源码分析
阅读量:6957 次
发布时间:2019-06-27

本文共 2056 字,大约阅读时间需要 6 分钟。

gevent是一个异步I/O框架,当遇到I/O操作的时候,会自动切换任务,从而能异步地完成I/O操作

但是在测试的情况下,可以使用sleep函数来让gevent进行任务切换。示例如下:

import geventdef 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协程
工作流程如图:
012118009219195.png

总结

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()函数接着上次执行的地方执行

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

转载于:https://www.cnblogs.com/eric-nirnava/p/geventsleep.html

你可能感兴趣的文章
wecenter屏蔽模板目录,防止别人访问到
查看>>
centos7源码搭建lamp基于模块化方式
查看>>
强大的nmcli命令独家揭秘
查看>>
信息系统项目管理师教程第3版教程 2017年9月出版
查看>>
StratoIO WebPrinter控件的下载与安装的步骤介绍
查看>>
SSH隧道
查看>>
virtualbox设置ubuntu的共享目录
查看>>
安卓调用webservice的一种方式及需注意的问题
查看>>
DIY 微信HD版共享
查看>>
python入门(四)python对文件的操作
查看>>
C# 使用接口进行排序
查看>>
干货!APP推广全周期解决方案 只需做好这6步
查看>>
存储基础网络面临的几大问题
查看>>
高效|五大模式和两大创新,看懂智能制造具体呈现
查看>>
LNMP动态网站部署架构 Linux + Nginx 配置Nginx服务
查看>>
cai
查看>>
电力变压器胶模时要注意到哪几点问题?中港扬盛提醒
查看>>
Linux 高可用(HA)集群之keepalived详解
查看>>
parse AST with Clang-example
查看>>
面向切面编程(AOP模式)
查看>>