- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Python并发编程协程(Coroutine)之Gevent详解由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
Gevent官网文档地址:http://www.gevent.org/contents.html 。
基本概念 。
我们通常所说的协程Coroutine其实是corporateroutine的缩写,直接翻译为协同的例程,一般我们都简称为协程.
在linux系统中,线程就是轻量级的进程,而我们通常也把协程称为轻量级的线程即微线程.
进程和协程 。
下面对比一下进程和协程的相同点和不同点:
相同点:
我们都可以把他们看做是一种执行流,执行流可以挂起,并且后面可以在你挂起的地方恢复执行,这实际上都可以看做是continuation,关于这个我们可以通过在linux上运行一个hello程序来理解:
shell进程和hello进程:
开始,shell进程在运行,等待命令行的输入 。
执行hello程序,shell通过系统调用来执行我们的请求,这个时候系统调用会讲控制权传递给操作系统。操作系统保存shell进程的上下文,创建一个hello进程以及其上下文并将控制权给新的hello进程.
hello进程终止后,操作系统恢复shell进程的上下文,并将控制权传回给shell进程 。
shell进程继续等待下个命令的输入 。
当我们挂起一个执行流的时,我们要保存的东西:
栈,其实在你切换前你的局部变量,以及要函数的调用都需要保存,否则都无法恢复 。
寄存器状态,这个其实用于当你的执行流恢复后要做什么 。
而寄存器和栈的结合就可以理解为上下文,上下文切换的理解:
CPU看上去像是在并发的执行多个进程,这是通过处理器在进程之间切换来实现的,操作系统实现这种交错执行的机制称为上下文切换 。
操作系统保持跟踪进程运行所需的所有状态信息。这种状态,就是上下文.
在任何一个时刻,操作系统都只能执行一个进程代码,当操作系统决定把控制权从当前进程转移到某个新进程时,就会进行上下文切换,即保存当前进程的上下文,恢复新进程的上下文,然后将控制权传递到新进程,新进程就会从它上次停止的地方开始.
不同点:
执行流的调度者不同,进程是内核调度,而协程是在用户态调度,也就是说进程的上下文是在内核态保存恢复的,而协程是在用户态保存恢复的,很显然用户态的代价更低 。
进程会被强占,而协程不会,也就是说协程如果不主动让出CPU,那么其他的协程,就没有执行的机会.
对内存的占用不同,实际上协程可以只需要4K的栈就足够了,而进程占用的内存要大的多 。
从操作系统的角度讲,多协程的程序是单进程,单协程 。
线程和协程 。
既然我们上面也说了,协程也被称为微线程,下面对比一下协程和线程:
线程之间需要上下文切换成本相对协程来说是比较高的,尤其在开启线程较多时,但协程的切换成本非常低.
同样的线程的切换更多的是靠操作系统来控制,而协程的执行由我们自己控制 。
我们通过下面的图更容易理解:
从上图可以看出,协程只是在单一的线程里不同的协程之间切换,其实和线程很像,线程是在一个进程下,不同的线程之间做切换,这也可能是协程称为微线程的原因吧 。
继续分析协程:
Gevent 。
Gevent是一种基于协程的Python网络库,它用到Greenlet提供的,封装了libevent事件循环的高层同步API。它让开发者在不改变编程习惯的同时,用同步的方式写异步I/O的代码.
使用Gevent的性能确实要比用传统的线程高,甚至高很多。但这里不得不说它的一个坑:
Monkey-patching,我们都叫猴子补丁,因为如果使用了这个补丁,Gevent直接修改标准库里面大部分的阻塞式系统调用,包括socket、ssl、threading和select等模块,而变为协作式运行。但是我们无法保证你在复杂的生产环境中有哪些地方使用这些标准库会由于打了补丁而出现奇怪的问题 。
第三方库支持。得确保项目中用到其他用到的网络库也必须使用纯Python或者明确说明支持Gevent 。
既然Gevent用的是Greenlet,我们通过下图来理解greenlet
每个协程都有一个parent,最顶层的协程就是man thread或者是当前的线程,每个协程遇到IO的时候就把控制权交给最顶层的协程,它会看那个协程的IO event已经完成,就将控制权给它.
下面是greenlet一个例子 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
from
greenlet
import
greenlet
def
test1(x,y):
z
=
gr2.switch(x
+
y)
print
(z)
def
test2(u):
print
(u)
gr1.switch(
42
)
gr1
=
greenlet(test1)
gr2
=
greenlet(test2)
gr1.switch(
"hello"
,
'world'
)
|
greenlet(run=None, parent=None): 创建一个greenlet实例. gr.parent:每一个协程都有一个父协程,当前协程结束后会回到父协程中执行,该 属性默认是创建该协程的协程. gr.run: 该属性是协程实际运行的代码. run方法结束了,那么该协程也就结束了. gr.switch(*args, **kwargs): 切换到gr协程. gr.throw(): 切换到gr协程,接着抛出一个异常. 。
下面是gevent的一个例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
import
gevent
def
func1():
print
(
"start func1"
)
gevent.sleep(
1
)
print
(
"end func1"
)
def
func2():
print
(
"start func2"
)
gevent.sleep(
1
)
print
(
"end func2"
)
gevent.joinall(
[
gevent.spawn(func1),
gevent.spawn(func2)
]
)
|
关于gevent中队列的使用 。
gevent中也有自己的队列,但是有一个场景我用的过程中发现一个问题,就是如果我在协程中通过这个q来传递数据,如果对了是空的时候,从队列获取数据的那个协程就会被切换到另外一个协程中,这个协程用于往队列里put放入数据,问题就出在,gevent不认为这个放入数据为IO操作,并不会切换到上一个协程中,会把这个协程的任务完成后在切换到另外一个协程。我原本想要实现的效果是往对了放入数据后就会切换到get的那个协程。(或许我这里理解有问题)下面是测试代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
import
gevent
from
gevent.queue
import
Queue
def
func():
for
i
in
range
(
10
):
print
(
"int the func"
)
q.put(
"test"
)
def
func2():
for
i
in
range
(
10
):
print
(
"int the func2"
)
res
=
q.get()
print
(
"--->"
,res)
q
=
Queue()
gevent.joinall(
[
gevent.spawn(func2),
gevent.spawn(func),
]
)
|
这段代码的运行效果为:
如果我在fun函数的q.put("test")后面添加gevent.sleep(0),就会是如下效果:
原本我预测的在不修改代码的情况下就应该是第二个图的结果,但是实际却是第一个图的结果(这个问题可能是我自己没研究明白,后面继续研究) 。
关于Gevent的问题 。
就像我上面说的gevent和第三方库配合使用会有一些问题,可以总结为: python协程的库可以直接monkey path C写成的库可以采用豆瓣开源的greenify来打patch(这个功能自己准备后面做测试) 。
不过总的来说gevent目前为止还是有很多缺陷,并且不是官网标准库,而在python3中有一个官网正在做并且在3.6中已经稳定的库asyncio,这也是一个非常具有野心的库,非常建议学习,我也准备后面深入了解 。
总结 。
以上就是本文关于Python并发编程协程(Coroutine)之Gevent详解的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持! 。
原文链接:http://www.cnblogs.com/zhaof/p/7536569.html 。
最后此篇关于Python并发编程协程(Coroutine)之Gevent详解的文章就讲到这里了,如果你想了解更多关于Python并发编程协程(Coroutine)之Gevent详解的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我的图书馆是否有任何“官方”方式可以测试 gevent monkey 补丁是否已激活? 最佳答案 我不知道官方方法,但这是一种方法。 注意 os.fork 在猴子修补时发生变化: >>> import
存储在 threading.local 中的数据对于特定的协同例程来说是唯一的,还是对于 Python 线程来说仍然是唯一的? 最佳答案 根据 gevent.monkey 的文档 http://www
经过一整天的黑客攻击并试图找出问题所在后,我问了这个问题。 我想使用 requests 包发送请求,并以非阻塞模式进行。 为此,我使用了很好的 gevent 包及其猴子修补功能,我已经使用它很长时间了
我正在把我的头发拉到这个上面。我正在尝试使用 zeromq 和 gevent 获取最简单的示例。我将此脚本更改为使用 PUB/SUB 套接字,当我运行它时,“服务器”套接字将永远循环。如果我取消注释
我正在使用 Flask 和 Flask-SocketIO 构建 Web 接口(interface)/数据 API 以进行 websocket 通信。我想开始转向使用 Gevent/Gevent-web
假设 tasks 是 Greenlet 对象的列表。现在有什么区别 gevent.wait(tasks) 和 gevent.joinall(tasks) ? 最佳答案 不多! joinall 实际上在
我有一个使用线程的 python bottle 应用程序。因为我正在使用 monkey.patch ,线程正在阻止应用程序执行(从线程触发的对话框阻止瓶路由响应客户端,直到被关闭。) 这里的一项小研究
在 gevent monkey 补丁中,我没有看到任何关于默认文件对象的操作。如何在基于 gevent 的程序中使用异步文件读/写? 最佳答案 您可以使用 1.0b3 中可用的 gevent 的 fi
过去几天,我一直在尝试将事件流集成到我的 Flask 应用程序中,在我的本地测试中取得了不错的结果,但在我的服务器上使用 uWSGI 运行该应用程序时却有些糟糕。我的代码基本上建立在 example
来自 gevent 文档: The greenlets all run in the same OS thread and are scheduled cooperatively. 那么是否仍然需要使
我在 Gevent 池中执行几十个 HTTP 请求。 目标是在请求失败时重试一次,但仅重试一次。否则,它应该抛出异常。 我如何使用 at pool 编写 gevent 代码以支持在失败时重新运行一次
简而言之,我的问题是:我怎么知道某个特定的 Python 包何时与 gevent 兼容(至少与 gevent.monkey.patch_all 兼容)? 其实我想知道是否http-parser库与 g
为什么 gevent 会抛出这个错误?在 ipython、ubuntu 13 中运行它 In [1]: from gevent import monkey In [2]: monkey.patch_a
在我基于 gevent 的程序中,我在某处有一个线程,它陷入了一个循环,类似于: while True: gevent.sleep(0) 我怎么知道这是哪个线程?是否可以列出(并获取堆栈跟踪)
运行 gevent 的 WSGIServer 时,我遇到了一些非常奇怪的行为。似乎每个通过的请求的方法都被错误地解释了.. 如果我发送以下请求: requests.get('http://localh
我的项目正在使用 gevnet(这对我来说是新的)来实现 Thrift 服务器。 我正在阅读代码并从其文档中学习。下面的代码片段在我的项目中: TSocket.socket = gevent.sock
我尝试在我的 osX (el capitan) 上安装 Odoo。然后我下载了这个包,当我尝试使用 pip install -r requirements.txt 安装 3rf party lib 时
我有多个 greenlets 在一个公共(public)套接字上发送。是否保证通过 socket.sendall 发送的每个包都很好地分开,或者我是否必须在每次调用 sendall 之前获取锁。 所以
我刚开始使用 virtualenv,但我正在尝试安装 gevent在 virtualenv 环境中(我正在运行 Windows)。当我在 virtualenv 中使用 PIP 时,出现此错误: MyE
目前我正在使用带有发布-订阅模式的 zeromq,我有一个工作人员要发布和许多 (8) 个订阅者(都将订阅)相同的模式。 现在我尝试使用多处理来生成订阅者,它可以正常工作。我缺少一些消息。 我使用多处
我是一名优秀的程序员,十分优秀!