摘要:怎么记录呢这里就可以回到上面我们需要了解的几个栈操作了。这个时候,如果函数执行完了想去执行函数的时候,就按照这个流程再保存,然后再把刚才存放的函数给复制到栈中。
gevent文档:that uses greenlet to provide a high-level synchronous API.
意思是:使用greenlet来提供高级同步API。
那greenlet又是怎样一种机制呢?这个后面会稍微简单的讲一下原理,不过在后续的几篇会着重分析它的源码。在开始之前先看看提到的几个关键字『高级』、『同步』,高级不高级暂时还不知道,毕竟在实际生产环境中才看得到效果。而同步这个概念,不知道大家理解深不深刻,总之我当初理解不是很深刻,所以遇到了就聊聊呗!
同步不明白为什么会叫同步,总是让我有一种错觉,让我联想到的第一个词汇就是同时进行。不知道大家是不是和我一样,至少我看到同步是不会去想到它的正确意思。我曾一度怀疑是不是翻译错了,但是在质疑别人错之前,应该先质疑一下自己。所以我开始怀疑我的语文没学好,然后我去查了一下汉语词典,结果就悲剧了,我已经开始怀疑我的人生了。 %>_<%
同步:指两个或两个以上随时间变化的量在变化过程中保持一定的相对关系。
《光明日报》1984.6.2:“城市改革的步子要加快,要从解决国家与企业、企业与职工的关系入手,把适合于当前情况的各项改革措施初步配起套来,同步进行。
从上面引用的话应该可以看出,国家与企业、企业与职工,都是两个相对的关系,有着各自的利益。但是他们必须合作才能完成一个共同目标,例如买票看电影。这个场景有两个动作需要完成,一个出票、一个买票,只有出票才能买票,并且这个买票的人还只能傻等,不能做其他事。
同步真心不是同时进行,所以还是要多读书啊!尤其是语文!
为什么这里会讲到同步呢?其实不讲也是可以的,和理解greenlet原理没有多大关系,但是和理解gevent的特性有那么点关系,因为听别人说gevent能够让你用这种同步的代码写出异步的感觉,同步的代码好理解也好处理。我没有验证过,所以我也不知道,真正的原因是感觉这个概念很容易混淆,所以就来讲讲了。
Greenlet还记得在协程篇中学习的吗?花了较长篇幅讲协程及从其子例程演变的过程。而greenlet就是python中实现Coroutine「协程」的一个基础库。前面我们了解了协程的相关概念及思想,但我们还不知道它在底层是怎样实现的。只知道它有个特别的地方,就是能够和进程、线程一样保存上下文,那协程的上下文是怎样保存的呢?
源码初探typedef struct _greenlet { PyObject_HEAD char* stack_start; char* stack_stop; char* stack_copy; intptr_t stack_saved; struct _greenlet* stack_prev; struct _greenlet* parent; PyObject* run_info; struct _frame* top_frame; int recursion_depth; PyObject* weakreflist; PyObject* exc_type; PyObject* exc_value; PyObject* exc_traceback; PyObject* dict; } PyGreenlet;
上面给出的是greenlet的结构体定义,有兴趣的可以去下一份源代码看看,里面也有很好的解释。
这里我们主要了解一下关于堆栈的几个操作,从命名规则就可以找到我们想要的东西,就是它们几个了:stack_start、stack_stop、stack_copy、stack_saved。
其实仔细想想,你会发现非常的简单。假设有『函数A』和『函数B』,『函数A』进栈执行到一半的时候需要调用『函数B』,没事这个简单,我们可以将『函数B』进栈(图stack-01)。
好了『函数B』进栈了,可它现在又想调用『函数A』了,怎么办?让『函数A』进栈(图stack-02),这个其实就是普通的函数调用,此『函数A』并不是第一次进栈的那个『函数A』,而是重新在栈中创建的一个实例,该实例的数据并不是之前『函数A』的数据,可是我想要之前『函数A』的数据啊!所以这个方法并不能实现我们想要的切换。
那怎样才能做到我们想要的呢?很简单,既然『函数B』完成了当前的任务,它就应该退出来了,但并不是直接出栈,而是通过某种方式将现有的state给记录下来,方便下次用到的时候能够找到。怎么记录呢?这里就可以回到上面我们需要了解的几个栈操作了。
首先,每个greenlet都有属于它们自己的stack_start和stack_stop,通过这两个可以找到你准备出栈的greenlet也就是『函数B』的所有数据,之后再调用PyMem_Realloc这个方法,就可以在heap中创建一个内存空间用来存放『函数B』,地址为stack_copy,大小为stack_saved字节,通过这两个就可以定位到你存放的『函数B』的数据了(图stack-03)。
之后变成下面(图stack-04)这样了。这个时候,如果『函数A』执行完了想去执行『函数B』的时候,就按照这个流程再保存,然后再把刚才存放的『函数B』给复制到C栈中。因为已经知道了stack_copy和stack_saved,所以也就不怕找不到它了。
整个greenlet切换流程大概就是这个样子了,不过到了这里不知道大家会不会有些疑问,虽然保存了stack_copy和stack_saved,但是『函数B』已经出栈了,不是就没有这个对象了吗?那这两个参数是由谁来保存呢?
其实这个对象从来就没有被销毁,只要这个greenlet没有正常退出,它的对象就一直都存在着,因为Greenlet还维护着一个链表,它保存着所有没有彻底退出的greenlet对象,『函数B』出栈并不是完全退出了,只是不参与这次行动。具体的细节就要等到下一篇或者下下篇的源码剖析来讲解了。
因为最近工作比较忙,换了一个部门,加上之前对源码理解不够,也就不敢随便发表。当然现在发表的也不敢说绝对正确,这些仅仅是我个人的理解,欢迎大神来指正。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/37354.html
摘要:不过不管怎样,就当多学习了一些其他知识,况且分析源码肯定少不了对这些知识的了解。官方文档描述里面提到了,在这个模块中指的就是,一般称它为方法列表。返回一个指向它创建的模块对象的指针。 showImg(http://young-py.github.io/imgs/yyzt3.jpg); 在greenlet篇中只是简单讲述了一下greenlet原理,不知道有没有人对源码感兴趣的,不过我还...
摘要:一旦有事件产生可能是一次出现好多个事件,就会按照优先级依次调用每个事件的回调函数。注意,是有超时的,所以一些无法以文件描述符的形式存在的事件也可以有机会被触发。 这一篇主要想跟大家分享一下 Gevent 实现的基础逻辑,也是有同学对这个很感兴趣,所以贴出来跟大家一起分享一下。 Greenlet 我们知道 Gevent 是基于 Greenlet 实现的,greenlet 有的时候也被...
摘要:随着我们对于效率的追求不断提高,基于单线程来实现并发又成为一个新的课题,即只用一个主线程很明显可利用的只有一个情况下实现并发。作为的补充可以检测操作,在遇到操作的情况下才发生切换协程介绍协程是单线程下的并发,又称微线程,纤程。 引子 之前我们学习了线程、进程的概念,了解了在操作系统中进程是资源分配的最小单位,线程是CPU调度的最小单位。按道理来说我们已经算是把cpu的利用率提高很多了。...
摘要:所以与多线程相比,线程的数量越多,协程性能的优势越明显。值得一提的是,在此过程中,只有一个线程在执行,因此这与多线程的概念是不一样的。 真正有知识的人的成长过程,就像麦穗的成长过程:麦穗空的时候,麦子长得很快,麦穗骄傲地高高昂起,但是,麦穗成熟饱满时,它们开始谦虚,垂下麦芒。 ——蒙田《蒙田随笔全集》 上篇论述了关于python多线程是否是鸡肋的问题,得到了一些网友的认可,当然也有...
摘要:协程,又称微线程,纤程。最大的优势就是协程极高的执行效率。生产者产出第条数据返回更新值更新消费者正在调用第条数据查看当前进行的线程函数中有,返回值为生成器库实现协程通过提供了对协程的基本支持,但是不完全。 协程,又称微线程,纤程。英文名Coroutine协程看上去也是子程序,但执行过程中,在子程序内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。 最大的优势就是协程极高...
阅读 2871·2021-11-23 09:51
阅读 977·2021-09-26 09:55
阅读 3835·2021-09-22 14:58
阅读 1319·2021-09-08 09:35
阅读 1043·2021-08-26 14:16
阅读 853·2019-08-23 18:17
阅读 1990·2019-08-23 16:45
阅读 673·2019-08-23 15:55