首页 热点资讯 义务教育 高等教育 出国留学 考研考公

pyhton协程-gevent

发布网友 发布时间:2024-12-12 07:52

我来回答

1个回答

热心网友 时间:2024-12-12 07:53

线程模型

线程的实现模型主要由3种:内核级线程模型、用户级线程模型、混合型线程模型。它们最大的区别在于线程与内核调度实体KSE(Kernel Scheduling Entity)之间的对应关系,可以被操作系统内核调度的对象实体,也称为内核级线程,操作系统内核最小调度单元。

英文叫Coroutine,通俗点叫用户态线程,但区别于线程的抢占式调度,使用的是协作调度,从一个状态开始一条执行流在多个协程间切换,好像多个协程写作完成一项任务。

关于生成器,我们知道是使用yield的迭代器具体代码:

这个执行过程就是当我们生成一个a的generator时,代码已经执行到yield,程序被挂住,然后调用一次next(a)此时继续执行返回yield后面的i。我们能在通过next(a)来控制它的执行流程,从而实现不同子程序的之间的交替执行。

在python的PEP-342里有对yield做了增强,能够传入值,能够在try-finally里使用等等,是生成器更能被用作协程。

协程,协作式调度的,而不是抢占式调度,greenlet是python的一个C扩展,能够提供自行调度的协程,他比yield协程的好处就是能够在一个协程传递参数给另一个协程(在switch函数中传值,虽然yield也能给协程传值,但是我们是必须在主函数的调度算法里。

上面共启动了两个greenlet协程,首先在主线程中通过gr1.switch()切换到test1函数,执行到gr2.switch()我们切换到了函数test2,随后又一次切换到了test1,最后当test1函数退出将直接返回到主线程所以我们能看到的输出就是:

多个greenlet可以共存在一个线程中,但是任意时刻一个线程中只有一个greenlet在运行。

为了让另一个greenlet运行,当前运行的greenlet必须放弃控制权,这叫做切换。切换时必须明确选择要让哪个greenlet来接替执行,当前的greenlet里调用目的地greenlet的switch方法来进行这样的切换。当切换发生时,当前greenlet的调用栈会被保存下来,目的地greenlet的调用栈被放到正确的位置,然后执行流在目的地greenlet上次切换的地方继续执行。

一个第三方协程库,基于greenlet实现自己的协程调度机制,实现的是协作式调度,区别于CPU的抢占式调度。

事件循环

hub是gevent中最重要的一部分

hub本身也是一个greenlet,在hub里我们运行着gevent的核心之一loop也就是事件循环,loop里封装着对应着底层libev的各个接口,当loop运行的时候,它将会用于处理所有的greenlet都可以通过get_hub获取当前的hub对象,若果不存在就重新创建一个。任何一个greenlet切换后都将进入到hub这个greenlet里,如果此时没有开启事件轮训就主动开启,之后便等待下一个greenlet的回调事件。

其中get_hub获取hub对象

在python的标准库里socket默认是阻塞模式,这种模式下它的许多函数都是阻塞的,其中就包括recv(),接收对端发送的数据。

转了一堆弯找到最终调用的是hub.wait(),首先声明一个Waiter对象,将waiter.switch函数传入到绑定了当前socket读事件的io watcher,当watcher监听到有数据到来时,就执行回调函数waiter.switch

到现在我们还不知道,在当前greenlet切出进hub中,到最后socket收到对端发送来消息,watcher回调成功切入到当前greenlet这段时间里,究竟gevent在干嘛。这个问题先放着,我们去探究另一个问题。

hub是怎么管理的greenlet的

我们可以从一个gevent的使用例子来看

看函数名,spawn就是产生一个什么东西,那现在用的是gevent那大概率是生产一个协程

看代码,我们发现就是初始化了一个Greenlet(继承自greenlet类,后者为c库greenlet接口的封装)实例,然后调用start最后返回它。那我们知道,我们首先去看这个类的初始化方法,发现有这么一行

这里的parent,参数就是我们的hub,没错,在同一线程内生成一个协程,都会指定它的parent为同一个hub,这样当协程执行完退出时就不会退出到主线程,而是会跳到我们的hub协程。

我们再看下start方法

到此也只是注册两个协程,并没有实际运行,我们再看下join(),简化代码我们看到这段。

到此也只是注册两个协程,并没有实际运行,我们再看下join(),简化代码我们看到这段。

其实这就是切换到hub这个greenlet,正如switch这个方法定义的:

我们去看下hub的self.run干了些什么,简化一下

大概逻辑就是执行我们事件循环loop,从Hub出发去寻找Loop最终代码落入到gevent.libev.corecffi.loop(如果是linux环境)再往里就调用c的lib库了,具体事件循环对象暴露哪些接口我们可以看gevent._interfaces.ILoop, loop.run()做的执行底层livev的事件监听,当有事件被触发,就会执行当时注册的回调函数waiter.switch这样就切换回指定的协程。

由python创始人亲自操刀的异步编程标准库,核心组件除了事件循环、Coroutine 还有任务(Task)、未来对象(Future)。

声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com