python gevent协程模块详解

简介: python gevent协程模块详解

python 模块 gevent 协程

文章目录

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-8
import time
from  greenlet import greenlet
def 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-8
import time
from  greenlet import greenlet
def 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-8
import time
from  greenlet import greenlet
def 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-8
import time
import gevent
def 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 time
import gevent
def 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 gevent
def test1():
  print 12
  gevent.sleep(0)
  print 34
def test2():
  print 56
  gevent.sleep(0)
  print 78
gevent.joinall([
  gevent.spawn(test1),
  gevent.spawn(test2),
])
$ python gevent2.py
12
56
34
78

第二个

import gevent
from gevent import Greenlet
def 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.py
Hello
I live!

5.4 同步vs异步

import gevent
import random
def 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.py
Synchronous:
Task 0 done
Task 1 done
Task 2 done
Task 3 done
Task 4 done
Asynchronous:
Task 2 done
Task 0 done
Task 1 done
Task 3 done
Task 4 done

参考:

相关文章
|
5天前
|
人工智能 自然语言处理 Shell
[oeasy]python070_如何导入模块_导入模块的作用_hello_dunder_双下划线
本文介绍了如何在Python中导入模块及其作用,重点讲解了`__hello__`模块的导入与使用。通过`import`命令可以将外部模块引入当前环境,增强代码功能。例如,导入`__hello__`模块后可输出“Hello world!”。此外,还演示了如何使用`help()`和`dir()`函数查询模块信息,并展示了导入多个模块的方法。最后,通过一个实例,介绍了如何利用`jieba`、`WordCloud`和`matplotlib`模块生成词云图。总结来说,模块是封装好的功能部件,能够简化编程任务并提高效率。未来将探讨如何创建自定义模块。
28 8
|
3天前
|
缓存 Shell 开发工具
[oeasy]python071_我可以自己做一个模块吗_自定义模块_引入模块_import_diy
本文介绍了 Python 中模块的导入与自定义模块的创建。首先,我们回忆了模块的概念,即封装好功能的部件,并通过导入 `__hello__` 模块实现了输出 "hello world!" 的功能。接着,尝试创建并编辑自己的模块 `my_file.py`,引入 `time` 模块以获取当前时间,并在其中添加自定义输出。
18 4
|
3月前
|
Python
Python Internet 模块
Python Internet 模块。
139 74
|
4月前
|
算法 数据安全/隐私保护 开发者
马特赛特旋转算法:Python的随机模块背后的力量
马特赛特旋转算法是Python `random`模块的核心,由松本真和西村拓士于1997年提出。它基于线性反馈移位寄存器,具有超长周期和高维均匀性,适用于模拟、密码学等领域。Python中通过设置种子值初始化状态数组,经状态更新和输出提取生成随机数,代码简单高效。
150 63
|
4月前
|
测试技术 Python
手动解决Python模块和包依赖冲突的具体步骤是什么?
需要注意的是,手动解决依赖冲突可能需要一定的时间和经验,并且需要谨慎操作,避免引入新的问题。在实际操作中,还可以结合使用其他方法,如虚拟环境等,来更好地管理和解决依赖冲突😉。
|
4月前
|
持续交付 Python
如何在Python中自动解决模块和包的依赖冲突?
完全自动解决所有依赖冲突可能并不总是可行,特别是在复杂的项目中。有时候仍然需要人工干预和判断。自动解决的方法主要是提供辅助和便捷,但不能完全替代人工的分析和决策😉。
|
2月前
|
Python
[oeasy]python057_如何删除print函数_dunder_builtins_系统内建模块
本文介绍了如何删除Python中的`print`函数,并探讨了系统内建模块`__builtins__`的作用。主要内容包括: 1. **回忆上次内容**:上次提到使用下划线避免命名冲突。 2. **双下划线变量**:解释了双下划线(如`__name__`、`__doc__`、`__builtins__`)是系统定义的标识符,具有特殊含义。
36 3
|
Python
164 python网络编程 - 协程(gevent版)
164 python网络编程 - 协程(gevent版)
73 0
|
数据采集 调度 Python
【Python零基础入门篇 · 36】:greenlet协程模块的使用、gevent模块的使用、程序打补丁、总结
【Python零基础入门篇 · 36】:greenlet协程模块的使用、gevent模块的使用、程序打补丁、总结
202 0
【Python零基础入门篇 · 36】:greenlet协程模块的使用、gevent模块的使用、程序打补丁、总结
|
调度 开发者 Python
【Python零基础入门篇 · 24】:协程和IO操作的简单理解、greenlet协程模块的使用、gevent模块的使用、程序打补丁、总结
【Python零基础入门篇 · 24】:协程和IO操作的简单理解、greenlet协程模块的使用、gevent模块的使用、程序打补丁、总结
131 0
【Python零基础入门篇 · 24】:协程和IO操作的简单理解、greenlet协程模块的使用、gevent模块的使用、程序打补丁、总结

热门文章

最新文章