- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章详解python之协程gevent模块由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
Gevent官网文档地址:http://www.gevent.org/contents.html 。
进程、线程、协程区分 。
我们通常所说的协程Coroutine其实是corporate routine的缩写,直接翻译为协同的例程,一般我们都简称为协程.
在linux系统中,线程就是轻量级的进程,而我们通常也把协程称为轻量级的线程即微线程.
进程和协程 。
下面对比一下进程和协程的相同点和不同点:
相同点:
寄存器状态,这个其实用于当你的执行流恢复后要做什么 。
而寄存器和栈的结合就可以理解为上下文,上下文切换的理解: CPU看上去像是在并发的执行多个进程,这是通过处理器在进程之间切换来实现的,操作系统实现这种交错执行的机制称为上下文切换 。
操作系统保持跟踪进程运行所需的所有状态信息。这种状态,就是上下文。 在任何一个时刻,操作系统都只能执行一个进程代码,当操作系统决定把控制权从当前进程转移到某个新进程时,就会进行上下文切换,即保存当前进程的上下文,恢复新进程的上下文,然后将控制权传递到新进程,新进程就会从它上次停止的地方开始.
不同点:
线程和协程 。
既然我们上面也说了,协程也被称为微线程,下面对比一下协程和线程:
协程只是在单一的线程里不同的协程之间切换,其实和线程很像,线程是在一个进程下,不同的线程之间做切换,这也可能是协程称为微线程的原因吧.
Gevent模块 。
Gevent是一种基于协程的Python网络库,它用到Greenlet提供的,封装了libevent事件循环的高层同步API。它让开发者在不改变编程习惯的同时,用同步的方式写异步I/O的代码.
简单示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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),
])
|
结果: 。
12 56 34 78 。
猴子补丁 Monkey patching 。
这个补丁是Gevent模块最需要注意的问题,有了它,才会让Gevent模块发挥它的作用。我们往往使用Gevent是为了实现网络通信的高并发,但是,Gevent直接修改标准库里面大部分的阻塞式系统调用,包括socket、ssl、threading和 select等模块,而变为协作式运行。但是我们无法保证你在复杂的生产环境中有哪些地方使用这些标准库会由于打了补丁而出现奇怪的问题.
一种方法是使用gevent下的socket模块,我们可以通过”from gevent import socket”来导入。不过更常用的方法是使用猴子布丁(Monkey patching)。使用猴子补丁褒贬不一,但是官网上还是建议使用”patch_all()”,而且在程序的第一行就执行.
1
2
3
4
5
6
7
|
from
gevent
import
monkey; monkey.patch_socket()
import
gevent
import
socket
urls
=
[
'www.baidu.com'
,
'www.gevent.org'
,
'www.python.org'
]
jobs
=
[gevent.spawn(socket.gethostbyname, url)
for
url
in
urls]
gevent.joinall(jobs, timeout
=
5
)
print
[job.value
for
job
in
jobs]
|
上述代码的第一行就是对socket标准库打上猴子补丁,此后socket标准库中的类和方法都会被替换成非阻塞式的,所有其他的代码都不用修改,这样协程的效率就真正体现出来了。Python中其它标准库也存在阻塞的情况,gevent提供了”monkey.patch_all()”方法将所有标准库都替换.
获取协程状态 。
另外,greenlet协程运行过程中发生的异常是不会被抛出到协程外的,因此需要用协程对象的”exception”属性来获取协程中的异常.
下面的例子很好的演示了各种方法和属性的使用.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
#!/usr/bin/env python
# _*_ coding utf-8 _*_
#Author: aaron
import
gevent
def
win():
return
'You win!'
def
fail():
raise
Exception(
'You failed!'
)
winner
=
gevent.spawn(win)
loser
=
gevent.spawn(fail)
print
(winner.started)
# True
print
(loser.started)
# True
# 在Greenlet中发生的异常,不会被抛到Greenlet外面。
# 控制台会打出Stacktrace,但程序不会停止
try
:
gevent.joinall([winner, loser])
except
Exception as e:
# 这段永远不会被执行
print
(
'This will never be reached'
)
print
(winner.ready())
# True
print
(loser.started)
# True
print
(winner.value)
# 'You win!'
print
(loser.value)
# None
print
(
'successful '
,winner.successful())
# True
print
(
'successful '
,loser.successful())
# False
# 这里可以通过raise loser.exception 或 loser.get()
# 来将协程中的异常抛出
print
(loser.exception)
|
协程运行超时控制 。
之前我们讲过在”gevent.joinall()”方法中可以传入timeout参数来设置超时,我们也可以在全局范围内设置超时时间:
1
2
3
4
5
6
7
8
9
10
11
|
import
gevent
from
gevent
import
Timeout
timeout
=
Timeout(
2
)
# 2 seconds
timeout.start()
def
wait():
gevent.sleep(
10
)
try
:
gevent.spawn(wait).join()
except
Timeout:
print
(
'Could not complete'
)
|
上例中,我们将超时设为2秒,此后所有协程的运行,如果超过两秒就会抛出”Timeout”异常。我们也可以将超时设置在with语句内,这样该设置只在with语句块中有效:
1
2
|
with Timeout(
1
):
gevent.sleep(
10
)
|
此外,我们可以指定超时所抛出的异常,来替换默认的”Timeout”异常。比如下例中超时就会抛出我们自定义的”TooLong”异常.
1
2
3
4
|
class
TooLong(Exception):
pass
with Timeout(
1
, TooLong):
gevent.sleep(
10
)
|
协程间通信 。
事件(Event)对象 。
greenlet协程间的异步通讯可以使用事件(Event)对象。该对象的”wait()”方法可以阻塞当前协程,而”set()”方法可以唤醒之前阻塞的协程。在下面的例子中,5个waiter协程都会等待事件evt,当setter协程在3秒后设置evt事件,所有的waiter协程即被唤醒.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
#!/usr/bin/env python
# _*_ coding utf-8 _*_
#Author: aaron
import
gevent
from
gevent.event
import
Event
evt
=
Event()
def
setter():
print
'Wait for me'
gevent.sleep(
3
)
# 3秒后唤醒所有在evt上等待的协程
print
"Ok, I'm done"
evt.
set
()
# 唤醒
def
waiter():
print
"I'll wait for you"
evt.wait()
# 等待
print
'Finish waiting'
gevent.joinall([
gevent.spawn(setter),
gevent.spawn(waiter),
gevent.spawn(waiter),
gevent.spawn(waiter),
gevent.spawn(waiter),
gevent.spawn(waiter)
])
|
AsyncResult事件 。
除了Event事件外,gevent还提供了AsyncResult事件,它可以在唤醒时传递消息。让我们将上例中的setter和waiter作如下改动
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#!/usr/bin/env python
# _*_ coding utf-8 _*_
#Author: aaron
from
gevent.event
import
AsyncResult
aevt
=
AsyncResult()
def
setter():
print
'Wait for me'
gevent.sleep(
3
)
# 3秒后唤醒所有在evt上等待的协程
print
"Ok, I'm done"
aevt.
set
(
'Hello!'
)
# 唤醒,并传递消息
def
waiter():
print
(
"I'll wait for you"
)
message
=
aevt.get()
# 等待,并在唤醒时获取消息
print
'Got wake up message: %s'
%
message
|
队列 Queue 。
队列Queue的概念相信大家都知道,我们可以用它的put和get方法来存取队列中的元素。gevent的队列对象可以让greenlet协程之间安全的访问。运行下面的程序,你会看到3个消费者会分别消费队列中的产品,且消费过的产品不会被另一个消费者再取到:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
#!/usr/bin/env python
# _*_ coding utf-8 _*_
#Author: aaron<br>
import
gevent
from
gevent.queue
import
Queue
products
=
Queue()
def
consumer(name):
#while not products.empty():
while
True
:
try
:
print
(
'%s got product %s'
%
(name, products.get_nowait()))
gevent.sleep(
0
)
except
gevent.queue.Empty:
break
print
(
'Quit'
)
def
producer():
for
i
in
range
(
1
,
10
):
products.put(i)
gevent.joinall([
gevent.spawn(producer),
gevent.spawn(consumer,
'steve'
),
gevent.spawn(consumer,
'john'
),
gevent.spawn(consumer,
'nancy'
),
])
|
注意:协程队列跟线程队列是一样的,put和get方法都是阻塞式的,它们都有非阻塞的版本:put_nowait和get_nowait。如果调用get方法时队列为空,则是不会抛出”gevent.queue.Empty”异常。我们只能使用get_nowait()的方式让气抛出异常.
信号量 。
信号量可以用来限制协程并发的个数。它有两个方法,acquire和release。顾名思义,acquire就是获取信号量,而release就是释放。当所有信号量都已被获取,那剩余的协程就只能等待任一协程释放信号量后才能得以运行:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
#!/usr/bin/env python
# _*_ coding utf-8 _*_
#Author: aaron
import
gevent
from
gevent.coros
import
BoundedSemaphore
sem
=
BoundedSemaphore(
2
)
def
worker(n):
sem.acquire()
print
(
'Worker %i acquired semaphore'
%
n)
gevent.sleep(
0
)
sem.release()
print
(
'Worker %i released semaphore'
%
n)
gevent.joinall([gevent.spawn(worker, i)
for
i
in
xrange
(
0
,
6
)])
|
上面的例子中,我们初始化了”BoundedSemaphore”信号量,并将其个数定为2。所以同一个时间,只能有两个worker协程被调度。程序运行后的结果如下:
Worker 0 acquired semaphore Worker 1 acquired semaphore Worker 0 released semaphore Worker 1 released semaphore Worker 2 acquired semaphore Worker 3 acquired semaphore Worker 2 released semaphore Worker 3 released semaphore Worker 4 acquired semaphore Worker 4 released semaphore Worker 5 acquired semaphore Worker 5 released semaphore 。
如果信号量个数为1,那就等同于同步锁.
协程本地变量 。
同线程类似,协程也有本地变量,也就是只在当前协程内可被访问的变量:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#!/usr/bin/env python
# _*_ coding utf-8 _*_
#Author: aaron
import
gevent
from
gevent.local
import
local
data
=
local()
def
f1():
data.x
=
1
print
data.x
def
f2():
try
:
print
data.x
except
AttributeError:
print
'x is not visible'
gevent.joinall([
gevent.spawn(f1),
gevent.spawn(f2)
])
|
通过将变量存放在local对象中,即可将其的作用域限制在当前协程内,当其他协程要访问该变量时,就会抛出异常。不同协程间可以有重名的本地变量,而且互相不影响。因为协程本地变量的实现,就是将其存放在以的”greenlet.getcurrent()”的返回为键值的私有的命名空间内.
多并发socket模型 。
服务器端:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
#!/usr/bin/env python
# _*_ coding utf-8 _*_
#Author: aaron
import
socket
import
gevent
from
gevent
import
socket, monkey
monkey.patch_all()
def
server(port):
s
=
socket.socket()
s.bind((
'0.0.0.0'
, port))
s.listen(
500
)
while
True
:
cli, addr
=
s.accept()
gevent.spawn(handle_request, cli)
def
handle_request(conn):
try
:
while
True
:
data
=
conn.recv(
1024
)
print
(
"recv:"
, data)
conn.send(data)
if
not
data:
conn.shutdown(socket.SHUT_WR)
except
Exception as ex:
print
(ex)
finally
:
conn.close()
if
__name__
=
=
'__main__'
:
server(
8001
)
|
当客户端连接上服务器端时,服务器端通过开辟一个协程与该客户端完成交互任务,同时由于使用了Gevent协程的方式,在每个客户端与服务器交互时,并不会影响到服务器端的工作.
客户端: 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#!/usr/bin/env python
# _*_ coding utf-8 _*_
#Author: aaron
import
socket
HOST
=
'localhost'
# The remote host
PORT
=
8001
# The same port as used by the server
s
=
socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
while
True
:
msg
=
bytes(
input
(
">>:"
), encoding
=
"utf8"
)
s.sendall(msg)
data
=
s.recv(
1024
)
# print(data)
print
(
'Received'
,
repr
(data))
# repr 格式化输出
s.close()
|
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我.
原文链接:http://www.cnblogs.com/cjaaron/p/9178083.html 。
最后此篇关于详解python之协程gevent模块的文章就讲到这里了,如果你想了解更多关于详解python之协程gevent模块的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我最近在我的机器上安装了 cx_Oracle 模块,以便连接到远程 Oracle 数据库服务器。 (我身边没有 Oracle 客户端)。 Python:版本 2.7 x86 Oracle:版本 11.
我想从 python timeit 模块检查打印以下内容需要多少时间,如何打印, import timeit x = [x for x in range(10000)] timeit.timeit("
我盯着 vs 代码编辑器上的 java 脚本编码,当我尝试将外部模块包含到我的项目中时,代码编辑器提出了这样的建议 -->(文件是 CommonJS 模块;它可能会转换为 ES6 模块。 )..有什么
我有一个 Node 应用程序,我想在标准 ES6 模块格式中使用(即 "type": "module" in the package.json ,并始终使用 import 和 export)而不转译为
我正在学习将 BlueprintJS 合并到我的 React 网络应用程序中,并且在加载某些 CSS 模块时遇到了很多麻烦。 我已经安装了 npm install @blueprintjs/core和
我需要重构一堆具有这样的调用的文件 define(['module1','module2','module3' etc...], function(a, b, c etc...) { //bun
我是 Angular 的新手,正在学习各种教程(Codecademy、thinkster.io 等),并且已经看到了声明应用程序容器的两种方法。首先: var app = angular.module
我正在尝试将 OUnit 与 OCaml 一起使用。 单元代码源码(unit.ml)如下: open OUnit let empty_list = [] let list_a = [1;2;3] le
我在 Angular 1.x 应用程序中使用 webpack 和 ES6 模块。在我设置的 webpack.config 中: resolve: { alias: { 'angular':
internal/modules/cjs/loader.js:750 return process.dlopen(module, path.toNamespacedPath(filename));
在本教程中,您将借助示例了解 JavaScript 中的模块。 随着我们的程序变得越来越大,它可能包含许多行代码。您可以使用模块根据功能将代码分隔在单独的文件中,而不是将所有内容都放在一个文件
我想知道是否可以将此代码更改为仅调用 MyModule.RED 而不是 MyModule.COLORS.RED。我尝试将 mod 设置为变量来存储颜色,但似乎不起作用。难道是我方法不对? (funct
我有以下代码。它是一个 JavaScript 模块。 (function() { // Object var Cahootsy; Cahootsy = { hello:
关闭。这个问题是 opinion-based 。它目前不接受答案。 想要改进这个问题?更新问题,以便 editing this post 可以用事实和引文来回答它。 关闭 2 年前。 Improve
从用户的角度来看,一个模块能够通过 require 加载并返回一个 table,模块导出的接口都被定义在此 table 中(此 table 被作为一个 namespace)。所有的标准库都是模块。标
Ruby的模块非常类似类,除了: 模块不可以有实体 模块不可以有子类 模块由module...end定义. 实际上...模块的'模块类'是'类的类'这个类的父类.搞懂了吗?不懂?让我们继续看
我有一个脚本,它从 CLI 获取 3 个输入变量并将其分别插入到 3 个变量: GetOptions("old_path=s" => \$old_path, "var=s" =
我有一个简单的 python 包,其目录结构如下: wibble | |-----foo | |----ping.py | |-----bar | |----pong.py 简单的
这种语法会非常有用——这不起作用有什么原因吗?谢谢! module Foo = { let bar: string = "bar" }; let bar = Foo.bar; /* works *
我想运行一个命令: - name: install pip shell: "python {"changed": true, "cmd": "python <(curl https://boot
我是一名优秀的程序员,十分优秀!