Python通过yield提供了对协程的基本支持,但是不完全。而第三方的gevent为Python提供了比较完善的协程支持。

gevent是第三方库,通过greenlet实现协程,其基本思想是:

当一个greenlet遇到IO操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO。

由于切换是在IO操作时自动完成,所以gevent需要修改Python自带的一些标准库,这一过程在启动时通过monkey patch完成:

from gevent import monkey; monkey.patch_socket()
import gevent def f(n):
for i in range(n):
print gevent.getcurrent(), i g1 = gevent.spawn(f, 5)
g2 = gevent.spawn(f, 5)
g3 = gevent.spawn(f, 5)
g1.join()
g2.join()
g3.join()

运行结果:

<Greenlet at 0x10e49f550: f(5)> 0
<Greenlet at 0x10e49f550: f(5)> 1
<Greenlet at 0x10e49f550: f(5)> 2
<Greenlet at 0x10e49f550: f(5)> 3
<Greenlet at 0x10e49f550: f(5)> 4
<Greenlet at 0x10e49f910: f(5)> 0
<Greenlet at 0x10e49f910: f(5)> 1
<Greenlet at 0x10e49f910: f(5)> 2
<Greenlet at 0x10e49f910: f(5)> 3
<Greenlet at 0x10e49f910: f(5)> 4
<Greenlet at 0x10e49f4b0: f(5)> 0
<Greenlet at 0x10e49f4b0: f(5)> 1
<Greenlet at 0x10e49f4b0: f(5)> 2
<Greenlet at 0x10e49f4b0: f(5)> 3
<Greenlet at 0x10e49f4b0: f(5)> 4

可以看到,3个greenlet是依次运行而不是交替运行。

要让greenlet交替运行,可以通过gevent.sleep()交出控制权:

def f(n):
for i in range(n):
print gevent.getcurrent(), i
gevent.sleep(0)

执行结果:

<Greenlet at 0x10cd58550: f(5)> 0
<Greenlet at 0x10cd58910: f(5)> 0
<Greenlet at 0x10cd584b0: f(5)> 0
<Greenlet at 0x10cd58550: f(5)> 1
<Greenlet at 0x10cd584b0: f(5)> 1
<Greenlet at 0x10cd58910: f(5)> 1
<Greenlet at 0x10cd58550: f(5)> 2
<Greenlet at 0x10cd58910: f(5)> 2
<Greenlet at 0x10cd584b0: f(5)> 2
<Greenlet at 0x10cd58550: f(5)> 3
<Greenlet at 0x10cd584b0: f(5)> 3
<Greenlet at 0x10cd58910: f(5)> 3
<Greenlet at 0x10cd58550: f(5)> 4
<Greenlet at 0x10cd58910: f(5)> 4
<Greenlet at 0x10cd584b0: f(5)> 4

3个greenlet交替运行,

把循环次数改为500000,让它们的运行时间长一点,然后在操作系统的进程管理器中看,线程数只有1个。

当然,实际代码里,我们不会用gevent.sleep()去切换协程,而是在执行到IO操作时,gevent自动切换,代码如下:

from gevent import monkey; monkey.patch_all()
import gevent
import urllib2 def f(url):
print('GET: %s' % url)
resp = urllib2.urlopen(url)
data = resp.read()
print('%d bytes received from %s.' % (len(data), url)) gevent.joinall([
gevent.spawn(f, 'https://www.python.org/'),
gevent.spawn(f, 'https://www.yahoo.com/'),
gevent.spawn(f, 'https://github.com/'),
])

运行结果:

GET: https://www.python.org/
GET: https://www.yahoo.com/
GET: https://github.com/
45661 bytes received from https://www.python.org/.
14823 bytes received from https://github.com/.
304034 bytes received from https://www.yahoo.com/.

从结果看,3个网络操作是并发执行的,而且结束顺序不同,但只有一个线程。

小结

使用gevent,可以获得极高的并发性能,但gevent只能在Unix/Linux下运行,在Windows下不保证正常安装和运行。

由于gevent是基于IO切换的协程,所以最神奇的是,我们编写的Web App代码,不需要引入gevent的包,也不需要改任何代码,仅仅在部署的时候,用一个支持gevent的WSGI服务器,立刻就获得了数倍的性能提升。具体部署方式可以参考后续“实战”-“部署Web App”一节。

来源:http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001407503089986d175822da68d4d6685fbe849a0e0ca35000

python之gevent模块实现协程的更多相关文章

  1. Gevent模块,协程应用

    Gevent官网文档地址:http://www.gevent.org/contents.html 进程.线程.协程区分 我们通常所说的协程Coroutine其实是corporate routine的缩 ...

  2. 线程池、进程池(concurrent.futures模块)和协程

    一.线程池 1.concurrent.futures模块 介绍 concurrent.futures模块提供了高度封装的异步调用接口 ThreadPoolExecutor:线程池,提供异步调用 Pro ...

  3. python并发编程之asyncio协程(三)

    协程实现了在单线程下的并发,每个协程共享线程的几乎所有的资源,除了协程自己私有的上下文栈:协程的切换属于程序级别的切换,对于操作系统来说是无感知的,因此切换速度更快.开销更小.效率更高,在有多IO操作 ...

  4. Python 多线程、进程、协程上手体验

    浅谈 Python 多线程.进程.协程上手体验 前言:浅谈 Python 很多人都认为 Python 的多线程是垃圾(GIL 说这锅甩不掉啊~):本章节主要给你体验下 Python 的两个库 Thre ...

  5. python网络-多任务实现之协程(27)

    一.协程 协程,又称微线程,纤程.英文名Coroutine. 协程不是进程,也不是线程,它就是一个函数,一个特殊的函数——可以在某个地方挂起,并且可以重新在挂起处继续运行.所以说,协程与进程.线程相比 ...

  6. python语法基础-并发编程-协程-长期维护

    ###############    协程    ############## # 协程 # 小知识点, # 协程和进程和线程一样都是实现并发的手段, # 开启一个线程,创建一个线程,还是需要开销, ...

  7. python并发编程之线程/协程

    python并发编程之线程/协程 part 4: 异步阻塞例子与生产者消费者模型 同步阻塞 调用函数必须等待结果\cpu没工作input sleep recv accept connect get 同 ...

  8. Python PEP 492 中文翻译——协程与async/await语法

    原文标题:PEP 0492 -- Coroutines with async and await syntax 原文链接:https://www.python.org/dev/peps/pep-049 ...

  9. python单线程,多线程和协程速度对比

    在某些应用场景下,想要提高python的并发能力,可以使用多线程,或者协程.比如网络爬虫,数据库操作等一些IO密集型的操作.下面对比python单线程,多线程和协程在网络爬虫场景下的速度. 一,单线程 ...

随机推荐

  1. Spring Boot(二)Application events and listeners

    一.自定义监听器: 1.创建: META-INF/spring.factories 2.添加: org.springframework.context.ApplicationListener=com. ...

  2. 我眼中的PageRank算法详解

    随着互联网的发展,网络上已有的网页数量庞大,并且每天都会有很多网页发布,如何权衡这些重要度的排名是一个很重要的问题.我们今天就来了解一下PageRank算法. 首先我们要来了解一下图的概念,请看图1. ...

  3. 深入浅出REST架构 REST架构概述

    http://www.nowamagic.net/librarys/veda/detail/885何为REST? REST是英文Representational State Transfer的缩写,中 ...

  4. unity, GetComponent<Renderer>().bounds.size vs GetComponent<MeshFilter>().sharedMesh.bounds.size

    GetComponent<MeshFilter>().sharedMesh.bounds.size获得的是未经缩放的大小. GetComponent<Renderer>().b ...

  5. 点滴积累【JS】---JS小功能(onmouseover实现选择月份)

    效果: 代码: <head runat="server"> <title></title> <style type="text/ ...

  6. Java小型知识点

    1. API 1.1 byte[].File.InputStream 互相转换 1.将File.FileInputStream 转换为byte数组: File file = new File(&quo ...

  7. 如何卸载Mysql

    mysql安装的时候会出现各种问题,尤其对新手,卸载不干净,重新安装会受到影响.也不能有点问题就重装系统吧!现在提供一种通过删除注册表的方式卸载mysql! 运行regedit打开注册表,搜索mysq ...

  8. Windows7 64位安装最新版本号MySQLserver

    Windows7 64位安装最新版本号MySQLserver 最近,一直在研究MySQL数据库.常常改动配置文件.导致MySQL数据库无法使用.不得不重复重装MySQL数据库.下面是在Windows7 ...

  9. django中使用POST方法报错 URL via POST, but the URL doesn't end in a slash

    该方式是因为URL路径没有使用slash(斜线"/")结尾造成的. 因此在使用POST的JavaScript函数的路径参数中,路径URL必须使用/结尾.

  10. IT 服务管理工具 iTop

    iTop,作为全面支持ITIL流程的一款ITSM工具,具有强大的ITSM功能,开源免费.简单易用. iTop,即IT运营门户(IT Operation Portal),是一个开源web应用程序,用于I ...