python gevent协程模块详解

网友投稿 480 2022-08-31


python gevent协程模块详解

文章目录

​​1. 进程、线程、协程区分​​

​​1.1 进程和协程​​​​1.2 线程和协程​​

​​2. 简介​​

​​2.1 greenlets​​

​​3. 特点​​​​4. 安装​​​​5. 示例​​

​​5.1 用greenlet执行一个函数​​​​5.2 创建协程任务​​​​5.3 同步和异步执行​​​​5.4 同步vs异步​​

1. 进程、线程、协程区分

我们通常所说的协程Coroutine其实是corporate routine的缩写,直接翻译为协同的例程,一般我们都简称为协程。

在linux系统中,线程就是轻量级的进程,而我们通常也把协程称为轻量级的线程即微线程。

1.1 进程和协程

下面对比一下进程和协程的相同点和不同点:

相同点:

相同点存在于,当我们挂起一个执行流的时,我们要保存的东西:栈, 其实在你切换前你的局部变量,以及要函数的调用都需要保存,否则都无法恢复 寄存器状态,这个其实用于当你的执行流恢复后要做什么

而寄存器和栈的结合就可以理解为上下文,上下文切换的理解: CPU看上去像是在并发的执行多个进程,这是通过处理器在进程之间切换来实现的,操作系统实现这种交错执行的机制称为上下文切换

操作系统保持跟踪进程运行所需的所有状态信息。这种状态,就是上下文。 在任何一个时刻,操作系统都只能执行一个进程代码,当操作系统决定把控制权从当前进程转移到某个新进程时,就会进行上下文切换,即保存当前进程的上下文,恢复新进程的上下文,然后将控制权传递到新进程,新进程就会从它上次停止的地方开始。

不同点:

执行流的调度者不同,进程是内核调度,而协程是在用户态调度,也就是说进程的上下文是在内核态保存恢复的,而协程是在用户态保存恢复的,很显然用户态的代价更低进程会被强占,而协程不会,也就是说协程如果不主动让出CPU,那么其他的协程,就没有执行的机会。对内存的占用不同,实际上协程可以只需要4K的栈就足够了,而进程占用的内存要大的多从操作系统的角度讲,多协程的程序是单进程,单协程

1.2 线程和协程

既然我们上面也说了,协程也被称为微线程,下面对比一下协程和线程:

线程之间需要上下文切换成本相对协程来说是比较高的,尤其在开启线程较多时,但协程的切换成本非常低。同样的线程的切换更多的是靠操作系统来控制,而协程的执行由我们自己控制。

协程只是在单一的线程里不同的协程之间切换,其实和线程很像,线程是在一个进程下,不同的线程之间做切换,这也可能是协程称为微线程的原因吧。

2. 简介

Gevent模块是一种基于协程的Python网络库,它用到Greenlet提供的,封装了libevent事件循环的高层同步API。它让开发者在不改变编程习惯的同时,用同步的方式写异步I/O的代码。

2.1 greenlets

如果想了解gevent的调度流程,最重要的是对greenlet有基本的了解。下面总结一些个人认为比较重要的点:

每一个greenlet.greenlet实例都有一个parent(可指定,默认为创生新的greenlet.greenlet所在环境),当greenlet.greenlet实例执行完逻辑正常结束、或者抛出异常结束时,执行逻辑切回到其parent可以继承greenlet.greenlet,子类需要实现run方法,当调用greenlet.switch方法时会调用到这个run方法

在gevent中,有两个类继承了greenlet.greenlet,分别是gevent.hub.Hub和gevent.greenlet.Greenlet。后文中,如果是greenlet.greenlet这种写法,那么指的是原生的类库greentlet,如果是greenlet(或者Greenlet)那么指gevent封装后的greenlet。

gevent中的主要模式, 它是以C扩展模块形式接入Python的轻量级协程。 全部运行在主程序操作系统进程的内部,但它们被程序员协作式地调度。

在任何时刻,只有一个协程在运行。

区别于multiprocessing、threading等提供真正并行构造的库, 这些库轮转使用操作系统调度的进程和线程,是真正的并行。

3. 特点

基于libev的快速事件循环(Linux上epoll,FreeBSD上kqueue)。基于greenlet的轻量级执行单元。API的概念和Python标准库一致(如事件,队列)。可以配合socket,ssl模块使用。能够使用标准库和第三方模块创建标准的阻塞套接字(gevent.monkey)。默认通过线程池进行DNS查询,也可通过c-are(通过GEVENT_RESOLVER=ares环境变量开启)。TCP/UDP/HTTP服务器子进程支持(通过gevent.subprocess)线程池

4. 安装

安装和依赖 依赖于greenlet library 支持python 2.6+ 、3.3+

pip install gevent

5. 示例

5.1 用greenlet执行一个函数

#coding:utf-8import timefrom greenlet import greenletdef eat(): print('魔降风云变 is eating') time.sleep(0.5) print('魔降风云变 finished eat')def sleep(): print('小马过河 is sleeping') time.sleep(0.5) print('小马过河 finished sleep')g1 = greenlet(eat)g1.switch()

--------------结果:

魔降风云变 is eating魔降风云变 finished eat

在一个任务函数中切换到另一个任务函数去执行,然后没有再切换回来

#coding:utf-8import timefrom greenlet import greenletdef eat(): print('魔降风云变 is eating') g2.switch() time.sleep(0.5) print('魔降风云变 finished eat')def sleep(): print('小马过河 is sleeping') time.sleep(0.5) print('小马过河 finished sleep')g1 = greenlet(eat)g2 = greenlet(sleep)g1.switch()

--------------结果:

魔降风云变 is eating小马过河 is sleeping小马过河 finished sleep

一个任务中切换到另一个任务,另一个任务执行完了再执行切换回到这个任务执行。实现两个任务间切换并都执行结束

#coding:utf-8import timefrom greenlet import greenletdef eat(): print('魔降风云变 is eating') g2.switch() time.sleep(0.5) print('魔降风云变 finished eat')def sleep(): print('小马过河 is sleeping') time.sleep(0.5) print('小马过河 finished sleep') g1.switch()g1 = greenlet(eat)g2 = greenlet(sleep)g1.switch()

------------结果:

魔降风云变 is eating小马过河 is sleeping小马过河 finished sleep魔降风云变 finished eat

5.2 创建协程任务

``python#coding:utf-8import timeimport geventdef eat(): print('魔降风云变 is eating') time.sleep(1) print('魔降风云变 finished eat')def sleep(): print('小马过河 is sleeping') time.sleep(1) print('小马过河 finished sleep')g1 = gevent.spawn(eat) # 创造一个协程任务

-----------结果:没有输出 协程遇到阻塞才切换,这里代码从上到下执行结束,没有遇到阻塞

import timeimport geventdef eat(): print('魔降风云变 is eating') time.sleep(1) print('魔降风云变 finished eat')def sleep(): print('小马过河 is sleeping') time.sleep(1) print('小马过河 finished sleep')g1 = gevent.spawn(eat) # 创造一个协程任务gevent.sleep(2) #加个gevent.sleep(2)就切换到eat执行里面的代码了

---------结果:

魔降风云变 is eating魔降风云变 finished eat

#加个gevent.sleep(2)就切换到eat执行里面的代码了。eat中间time.sleep(1)照样睡。

5.3 同步和异步执行

两个子任务之间的 切换也就是上下文切换。 创建Greenlets,gevent对Greenlet初始化提供了一些封装. 第一个

import geventdef test1(): print 12 gevent.sleep(0) print 34def test2(): print 56 gevent.sleep(0) print 78gevent.joinall([ gevent.spawn(test1), gevent.spawn(test2),])

$ python gevent2.py12563478

第二个

import geventfrom gevent import Greenletdef foo(message, n): gevent.sleep(n) print(message) thread1 = Greenlet.spawn(foo, "Hello", 1) thread2 = gevent.spawn(foo, "I live!", 2) thread3 = gevent.spawn(lambda x: (x+1), 2) threads = [thread1, thread2, thread3] gevent.joinall(threads)

第二个

$ python gevent3.pyHelloI live!

5.4 同步vs异步

import geventimport randomdef task(pid): gevent.sleep(random.randint(0,2)*0.001) print('Task %s done' % pid)def synchronous(): for i in xrange(5): task(i)def asynchronous(): threads = [gevent.spawn(task, i) for i in xrange(5)] gevent.joinall(threads) print('Synchronous:') synchronous() print('Asynchronous:') asynchronous()

$ python gevent3.pySynchronous:Task 0 doneTask 1 doneTask 2 doneTask 3 doneTask 4 doneAsynchronous:Task 2 doneTask 0 doneTask 1 doneTask 3 doneTask 4 done

参考连接:

​​https://softlns.github.io/2015/11/28/python-gevent/​​

​​http://codingdict.com/blog/article/2019/7/11/3560.html​​


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:python open函数文本操作详解(python是什么意思)
下一篇:Java Integer如何获取第一位和最后一位,并截取
相关文章

 发表评论

暂时没有评论,来抢沙发吧~