Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Python eventlet

Python eventlet

作者头像
周小董
发布于 2019-03-25 02:26:19
发布于 2019-03-25 02:26:19
4.7K00
代码可运行
举报
文章被收录于专栏:python前行者python前行者
运行总次数:0
代码可运行

eventlet是python库函数,一个是处理和网络相关的,另一个可以通过协程实现并发。所谓并发,就是开启了多个greenthread(绿色线程),并且对这些greenthread进行管理,以实现非阻塞式的I/O。eventlet为了实现“绿色线程”,竟然对python的和网络相关的几个标准库函数进行了改写,并且可以以补丁(patch)的方式导入到程序中,因为python的库函数只支持普通的线程,而不支持协程,eventlet称之为“绿化”。

一、什么是协程

1、协程是一条执行序列,拥有自己独立的栈、局部变量和指令指针,同时又与其他的协同程序共享全局变量和其他大部分东西

2、线程与协程主要区别:

一个多线程的程序可以同时运行几个线程,而协同程序需要彼此协作运行。

一个具有多个协程的程序在任何时刻只能运行一个协程,并且正在执行的协程只会在自己显示的挂起时,执行才会暂停。

二、协程的好处

1、每个协程都有自己私有的栈和局部变量

2、同一时间只有一个协程在运行,无需对全局变量进行加锁

3、顺序可控,完全是由程序控制执行的顺序。而通常多线程一旦启动,运行时序是没法预测的,因此通常会给测试所有的程序带来问题。

三、eventlet是什么,用来做什么

1、eventlet是Python库函数,一个是处理和网络相关的,另一个可以通过协程实现并发

2、在eventlet里,把“协程”叫做greenthread(绿色线程)。

所谓并发,就是开启了多个greenthread(绿色线程),并且对这些greenthread进行管理,以实现非阻塞式的I/O。

比如说用eventlet可以很方便的写一个性能很好的web服务器,或者是一个效率很高的网页爬虫,这都归功于eventlet的"绿色线程",以及对“绿色线程”的管理机制。eventlet为了实现“绿色线程”,竟然对python的和网络相关的几个标准库函数进行了改写,并且可以以补丁(patch)的方式导入到程序中,因为python的库函数只支持普通的线程,而不支持协程,eventlet称之为“绿化”。

本文首先讲述GreenLet,GreenThread,然后说明eventlet中几个常用的类

GreenLet

greenlet是一个称为协程(coroutine)的东西,有下面几个特点。 1.每个协程都有自己的私有stack及局部变量;  2.同一时间内只有一个协程在运行,故无须对某些共享变量加锁;  3.协程之间的执行顺序,完成由程序来控制;

总之,协程就是运行在一个线程内的伪并发方式,最终只有一个协程在运行,然后程序来控制执行的顺序。可以看下面的例子来理解上面的意思。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import greenlet

def test1(n):
    print("test1:",n)
    gr2.switch(32)
    print("test1: over")

def test2(n):
    print("test2:",n )
    gr1.switch(23)
    print("test2: over")

greenlet = greenlet.greenlet
current = greenlet.getcurrent()
gr1 = greenlet(test1,current)
gr2 = greenlet(test2,current)
gr1.switch(2)

这段程序的执行结果如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
test1: 2  
test2: 32  
test1: over  

整个程序的过程很直白,首先创建两个协程,创建的过程传入了要执行的函数和父greenlet,然后调用其中的一个协程的switch函数,并且传递参数进去,就开始执行test1,然后到了gr2.switch(32)语句,切换到test2函数来,最后又切换回去。最终test1运行结束,回到父greenlet中,执行结束。这个过程就是始终只有一个协程在运行,函数的执行流由程序自己来控制。这个过程在上面的链接中描述的更加具体。

参考资料 : http://greenlet.readthedocs.org/en/latest/

GreenThread

那么在eventlet中对greenlet进行了简单的封装,就成了GreenThread,并且上面的程序还会引来一个问题,如果我们想要写一个协程,那到底该如何来控制函数的执行过程了,如果协程多了,控制岂不是很复杂了。带着这个问题来看eventlet的实现。

在eventLet中,GreenThread的调度是通过hub来实现的。hub是EventLet的时间循环,用来调度IO事件和GreenThread。  Hub有不同版本的实现,如epolls,poll,selects,pyevent版,不同版本的性能特性不同,(具体见http://eventlet.net/doc/hubs.html#eventlet.hubs.use_hub)。可使用eventlet.hubs.use_hub(hub=None)来配置使用的hub,其中,传入的参数为选用的hub的名字。

hub是线程局部类实例,即每个线程都有一个hub实现,用来管理该线程中的co-rountine的调度。你可以为每个线程选用合适的hub(这样很麻烦,通常是:你为主线程选用一个hub,其他线程使用默认的hub)

hub工作原理:hub自己有一个GreenLet,作为MainLoop.当某个运行中的coroutine需要IO操作时,则该co-routine在hub中注册一个监听器(hub知道何时该co-routine wake up)。如果有其他可运行的co-routine,则hub MainLoop 切换到这些co-routine(via get_hub().switch())。如果这些co-routime完成了或是也需要IO,则由切换到hub main Loop中。这样,保证每个co-rountine都能被调度。

hub MAINLOOP 在第一个IO操作时启动。这种lazy模式使得不需要显示的调用dispatch函数。

eventlet官方的文档(http://eventlet.net/doc/basic_usage.html)

GreenPool

该模块提供对 greenthread 池的支持。

greenthread 池提供了一定数量的备用 greenthread ,有效限制了孵化 greenthread 过多导致的内存不足,当池子中没有足够的空闲 greenthread 时,孵化过程被暂停,只有当先前工作中的 greenthread 完成当前工作,才能为下一个任务做孵化准备。

class eventlet.greenpool.GreenPool(size=1000)

类主要方法:

    1. free()
    1. imap(function, *iterables)
    1. resize(new_size)
    1. running()
    1. spawn(function, *args, **kwargs)
    1. spawn_n(function, *args, **kwargs)
    1. starmap(function, iterable)
    1. waitall()
    1. waiting()

1. free() :返回当前对象中可用的greenthreads

如果为 0 或更少,那么 spawn() 和 spawn_n() 将会阻塞调用 greenthread 直到有新的可用的 greenthread 为止。至于为什么此处可能返回负值,请查看3. resize() 2. imap(function, *iterables) :效果等同于 itertools.imap() ,在并发和内存使用上等同于 starmap()

例如,可以非常方便地对文件做一些操作:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def worker(line):
    return do_something(line)
pool = GreenPool()
for result in pool.imap(worker, open("filename", 'r')):
    print(result)

3.resize(new_size) :改变当前允许同时工作的 greenthreads 最大数量 如果当前有多于 new_size 的 greenthreads 处于工作中,它们可以完成自己的执行,只不过此时不许任何的新 greenthreads 被分配。只有当足够数量的 greenthreads 完成自己的工作,然后工作中的 greenthreads 总数低于 new_size 时,新的 greenthreads 才能被分配。在此之前,free() 的返回值将会使负的。

4.running() :返回当前池子中正在执行任务的 greenthreads

5.spawn(function, *args, **kwargs) : 从当前的池子中孵化一个可用的greenthread,在这个 greenthread 中执行 function ,参数 *args, **kwargs 为传给 function 的参数。返回一个 GreenThread 对象,这个对象执行着 function ,可以通过该 GreenThread 对象获取 function 的返回值

如果当前池子中没有空余的 greenthread ,那么该方法阻塞直到有新的可用的 greenthreads 被释放。 该函数可以重用, function 可以调用同一个 GreenPool 对象的 spawn 方法,不用担心死锁。

6.spawn_n(function, *args, **kwargs) :创建一个 greenthread 来运行 function,效果等同于 spawn()。 只不过这个函数返回 None,即丢弃 function 的返回值

7.starmap(function, iterable) :等同于 itertools.starmap(),除了对于可迭代对象中的每一个元素,都会在一个 greenthread 里面执行 func 。 并发的上限由池子的容量限制。在实际的操作中, starmap() 消耗的内存与池子的容量成比例,从而格外适合遍历特别长的输入列表

8.waitall() ; 等待池子中的所有 greenthreads 完成工作

9.waiting() :返回当前等待孵化的 greenthreads 数

GreenPile

class eventlet.greenpool.GreenPile(size_or_pool=1000)  在它内部维护了一个GreenPool对象和一个Queue对象。这个GreenPool对象可以是从外部传递进来的,也可以是在类内部创建的,GreenPool对象主要是用来创建绿色线程的,即在GreenPile内部调用了GreenPool.spawn()方法。而Queue对象则是用来保spawn()方法的返回值的,即Queue中保存的是GreenThread对象。并且它还实现了next()方法,也就意味着GreenPile对象具有了迭代器的性质。所以如果我们要对绿色线程的返回值进行操作的话,用这个类是再好不过的了。

Event

class eventlet.event.Event

Event:在同一个线程中的协程通信的机制(an arbitrary number of coroutines can wait for one event from another)。Event和Queue类型,可看成只能容纳一条消息的Queue,但是和Queue不同的是:  (1)调用send() 不会取消对当前greenthread的调度;  (2)对一个event实例,只能调用一次send函数。如果你需要发送两条消息,则需要两个event对象。

一个使用实例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import eventlet

evt = eventlet.event.Event()
def baz(b):
    evt.send(b + 1)

_ = eventlet.spawn_n(baz, 3)
evt.wait()

函数说明:

  1. ready() :判断一个Event对象有没有发出过事件,如果调用 wait() 会立即返回一个事件结果,那么此处就返回真值。该方法用来避免需要等待一段时间才会发生的事件。例如,你可以将一堆事件放到一个Python列表中,然后重复地遍历他们,这是就可以调用 ready() 直到其中的一个事件返回True,然后就可以立刻调用 wait() 来获取它了。
  2. send(result=None, exc=None) :发送一个事件,可唤醒其他wait的协程。调用该方法的协程会立刻返回(不会阻塞)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import eventlet
evt = eventlet.event.Event()
def waiter():
    print('about to wait')
    result = evt.wait()
    print('waited for {0}'.format(result))
_ = eventlet.spawn(waiter)#about to wait
eventlet.sleep(0)
evt.send('a')#waited for a
eventlet.sleep(0)

需要注意的是:不同对同一个event实例调用多次send函数,这会导致抛出异常

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
>>> evt.send('whoops')
Traceback (most recent call last):
...
AssertionError: Trying to re-send() an already-triggered event.

send_exception(*args) :作用类似于 send() 方法,只不过向等待者发送的是一个异常。如果单个异常对象被传进来,它会在 wait() 方法被调用的时候重新抛出,生成一个新的堆栈轨迹。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
>>> from eventlet import event
>>> evt = event.Event()
>>> evt.send_exception(RuntimeError())
>>> evt.wait()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "eventlet/event.py", line 120, in wait
    current.throw(*self._exc)
RuntimeError

如果需要完整地保留堆栈轨迹,必须传入整个 sys.exc_info() 元组。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
>>> import sys
>>> evt = event.Event()
>>> try:
...     raise RuntimeError()
... except RuntimeError:
...     evt.send_exception(*sys.exc_info())
...
>>> evt.wait()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "eventlet/event.py", line 120, in wait
    current.throw(*self._exc)
  File "<stdin>", line 2, in <module>
RuntimeError

此时会在Event对象内部存储一个 traceback 对象,这可能会导致循环引用。详见 sys.exc_info() 的文档。

wait() :等待直到另一个协程调用 send() 。返回其他协程传递给 send() 方法的值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import eventlet

evt = eventlet.event.Event()
def wait_on():
    retval = evt.wait()
    print("waited for {0}".format(retval))
_ = eventlet.spawn(wait_on)
evt.send('result')#waited for result
eventlet.sleep(0)

举例

1、网络爬虫,用一个绿色线程池获取urls的信息。用到绿色线程池和imap()函数。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import eventlet,urllib.request
# from eventlet.green import urllib2

urls = [
    "http://www.google.com/intl/en_ALL/images/logo.gif",
    "http://python.org/images/python-logo.gif",
    "http://us.i1.yimg.com/us.yimg.com/i/ww/beta/y3.gif",
]

def fetch(url):
    # return urllib2.urlopen(url).read()
    return urllib.request.urlopen(url).read()

pool = eventlet.GreenPool(1000)#创建绿色线程池对象,可以指定数量
for body in pool.imap(fetch, urls): #协程根据指定要执行的函数依次执行获得url的信息
    print("got body", len(body))

2、socket服务器

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import eventlet

def handle(fd):
    print("client connected")
    while True:   #典型的读取文件的操作
        # pass through every non-eof line
        x = fd.readline()
        if not x: break
        fd.write(x)
        fd.flush()         #将文件内容刷新到硬盘中
        print("echoed", x)
    print("client disconnected")

print("server socket listening on port 8000")   #服务器监听程序,响应客户端的请求
server = eventlet.listen(('127.0.0.1', 8000))  # (IP地址, 端口) 元组的形式表示
pool = eventlet.GreenPool(200)    #绿色线程池,允许并行访问

while True:
    new_sock, address = server.accept() #阻塞,等待客户端连接   返回socket对象和客户端的  IP地址和端口号
    print("accepted", address)
    pool.spawn_n(handle, new_sock.makefile('rw'))  #创建新的绿色线程然后去执行

3、使用GreenPile的例子

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import eventlet
from eventlet.green import socket

def geturl(url):
    c = socket.socket()
    ip = socket.gethostbyname(url)
    c.connect((ip, 80))
    print('%s connected' % url)
    c.sendall('GET /\r\n\r\n')
    return c.recv(1024)

urls = ['www.google.com', 'www.yandex.ru', 'www.python.org']
pile = eventlet.GreenPile()
for x in urls:
    pile.spawn(geturl, x)

# 注意,如果函数引发任何异常,那么pile将充当函数返回值的集合,它们将在这里引发
for url, result in zip(urls, pile):
    print('%s: %s' % (url, repr(result)[:50]))

参考:https://blog.csdn.net/youyou1543724847/article/details/71404145 http://www.aboutyun.com/thread-20085-1-1.html https://github.com/eventlet/eventlet/

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019年01月21日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
python网络-多任务实现之协程(27)
协程不是进程,也不是线程,它就是一个函数,一个特殊的函数——可以在某个地方挂起,并且可以重新在挂起处继续运行。所以说,协程与进程、线程相比,不是一个维度的概念。
Se7eN_HOU
2019/06/26
8580
OpenStack服务的启动机制
基本所有的openstack服务都依赖 evenlet 完成各种并发任务,它的进程可分为两类: 1、 WSGIService: 接收和处理 http 请求,依赖eventlet.wsgi 的 wsgi server 处理 http 请求,比如nova-api 2、 Service: 接收和处理 rpc 请求,如 nova-operation等 无论是 WSGIService 还是 Service 类型的进程,每当接收到一个请求(http 或 rpc),都会在线程池中分配一个协程处理该请求
tunsuy
2022/10/27
6000
练习PYTHON之EVENTLET
eventlet是一个用来处理和网络相关的python库函数,而且可以通过协程来实现并发,在eventlet里,把“协程”叫做 greenthread(绿色线程)。所谓并发,就是开启了多个greenthread,并且对这些greenthread进行管理,以实现非阻塞式的 I/O。比如说用eventlet可以很方便的写一个性能很好的web服务器,或者是一个效率很高的网页爬虫,这都归功于eventlet的“绿色线程”,以及对“绿色线程”的管理机制。更让人不可思议的是,eventlet为了实现“绿色线程”,竟然对python的和网络相关的几个标准库函数进行了改写,并且可以以补丁(patch)的方式导入到程序中,因为python的库函数只支持普通的线程,而不支持协程,eventlet称之为“绿化”。
全栈程序员站长
2022/07/05
5380
12.python进程\协程\异步IO
进程 Python中的多线程无法利用多核优势 , 所以如果我们想要充分地使用多核CPU的资源 , 那么就只能靠多进程了 multiprocessing模块中提供了Process , Queue , Pipe , Lock , RLock , Event , Condition等组件 , 与threading模块有很多相似之处 1.创建进程 from multiprocessing import Process import time def func(name): time.sleep(2)
zhang_derek
2018/04/11
9520
Python 三程三器的那些事
    1.定义:把一个函数名当做实参传给另一个函数     2.返回值中包含函数名     3.下面使用高阶函数虽然可以实现装饰器的一些功能,但是违反了装饰器不能改变调用方式的原则,      以前使用bar()现在将调用方式改编成了test1(bar)就是将bar的函数名当做变量传给了test1()
IT茂茂
2020/03/19
1K0
2018年8月26日多协程编程总结
今天遇到的新单词: synchronous adj同步的 asynchronous  adj异步的 subscript n下标 split v分开 coroutine n协程
武军超
2018/09/27
7090
协程及Python中的协程
协程,又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程。(其实并没有说明白~)
用户1432189
2018/09/05
1.4K0
协程及Python中的协程
Python并发编程协程(Coroutine)之Gevent
Gevent官网文档地址:http://www.gevent.org/contents.html 基本概念 我们通常所说的协程Coroutine其实是corporate routine的缩写,直接翻译
coders
2018/01/04
1.7K0
Python并发编程协程(Coroutine)之Gevent
使用Python进行并发编程
让计算机程序并发的运行是一个经常被讨论的话题,今天我想讨论一下Python下的各种并发方式。
程序员迪迪
2022/01/13
1K0
Python 协程
协程是python个中另外一种实现多任务的方式,只不过比线程更小占用更小执行单元(理解为需要的资源)。 为啥说它是一个执行单元,因为它自带CPU上下文。这样只要在合适的时机, 我们可以把一个协程 切换到另一个协程。 只要这个过程中保存或恢复 CPU上下文那么程序还是可以运行的。
Devops海洋的渔夫
2019/05/31
8330
【PYTHON模块】:协程与greenl
作用:它拥有自己的寄存器上下文和栈,能保留上一次调用时的状态,可以随时暂停程序,随时切换回来。
py3study
2020/01/15
3940
python线程、协程
如果同一时间有多个线程操作同一数据可能会出现混乱现象,所以需要加线程锁保证同一时间只有一个线程操作这一数据,保证数据一致性
py3study
2020/01/13
3260
Python学习(十)---- python中的进程与协程
原文地址: https://blog.csdn.net/fgf00/article/details/52790360 编辑:智能算法,欢迎关注! 上期我们一起学习了python中的线程的相关知识
智能算法
2018/10/08
5770
线程,进程和协程
上述代码创建了10个“前台”,线程,然后控制器交给你了CPU,CPU根据指定算法进行调度,分片执行指令。
Wyc
2018/09/11
3720
python多任务—协程(一)
写在前面: 花了一周的时间,对协程做了一个简单的梳理,特别是异步编程asyncio库的使用,做了详细的说明。本文主要包括的知识点有:yield生成器的复习并实现协程的功能、greenlet库实现协程、gevent库实现协程、asyncio异步协程的介绍、异步协程的创建与运行、任务的创建与运行、并发运行gather/wait/as_complete/wait_for等方法的实现、异步协程的嵌套、await关键字的理解等等,这些都是基础。由于篇幅比较长,打算分为两篇,第二篇在介绍一下asyncio的其他用法。
全栈程序员站长
2022/09/14
1.7K0
gevent.hub.BlockingSwitchOutError: Impossible to call blocking function in the event loop callback
最近一个 python 项目中同时用到了 gevent 和 multiprocessing。在优雅退出的实现上,出现了一些预料之外的问题。
饶文津
2021/11/04
1.2K0
python异步并发框架
呵呵,这个标题有点大,其实只是想从零开始介绍一下异步的基础,以及 Python 开源异步并发框架的发展和互操作性。
py3study
2020/01/08
2.6K1
Python下的协程
协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。
py3study
2020/01/10
2730
Python 【基础面试题】
面试题仅做学习参考,学习者阅后也要用心钻研其中的原理,重要知识需要系统学习、透彻学习,形成自己的知识链。以下五点建议希望对您有帮助,早日拿到一份心仪的offer。
IT茂茂
2020/04/10
1.3K0
Python 【基础面试题】
[源码分析] 并行分布式任务队列 Celery 之 Timer & Heartbeat
Celery是一个简单、灵活且可靠的,处理大量消息的分布式系统,专注于实时处理的异步任务队列,同时也支持任务调度。
罗西的思考
2021/05/10
9560
[源码分析] 并行分布式任务队列 Celery  之 Timer & Heartbeat
相关推荐
python网络-多任务实现之协程(27)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验