协程的含义就不再提,在py2和py3的早期版本中,python协程的主流实现方法是使用gevent模块。由于协程对于操作系统是无感知的,所以其切换需要程序员自己去完成。

系列文章

基于生成器的简单协程

import time
def A():
for i in range(100):
print("----A---")
yield i
time.sleep(0.5) def B(c):
while True:
print("----B---")
try:
next(c)
except StopIteration:
break
else:
time.sleep(0.5) if __name__ == '__main__':
a = A()
B(a)

上面的例子并没有带来代码效率的提高,因为time.sleep()是同步阻塞操作;上例主要是为了说明协程的上下文切换原理。

greenlet的协程

greenlet模块也是程序显性主动控制协程切换,但是对原生做了一定的封装使得切换变得简单一些。

from greenlet import greenlet
import time def test1(gr,g):
for i in range(100):
print("---A--")
gr.switch(g, gr) # 切换到另一个协程执行
time.sleep(0.5) def test2(gr, g):
for i in range(100):
print("---B--")
gr.switch(g, gr)
# gr.throw(AttributeError)
time.sleep(0.5) if __name__ == '__main__':
# 创建一个协程1
gr1 = greenlet(test1)
# 创建一个协程2
gr2 = greenlet(test2)
# 启动协程
gr1.switch(gr2, gr1)

greenlet类主要有两个方法:

  • switch:用来切换协程;

  • throw():用来抛出异常同时终止程序;

gevent模块协程

  • gevent是在greenlet的基础上进行封装使得gevent变得更加的易用。

  • gevent采用了隐式启动事件循环,即在需要阻塞的时候开启一个专门的协程来启动事件循环;

  • 如果一个任务没有io操作,那么他会一直执行直到完成;其他协程没有执行的机会;

  • 自动识别io事件,放弃CPU控制时间;

# 一个补丁patch_all,注意要放在所有的import前面,其会将线程、进程替换成gevent框架,使得我们可以用同步编程的方式编写异步代码
from gevent import monkey;monkey.patch_all()
import gevent
import requests def target0(n):
print('--start---{}'.format(n))
res = requests.get('http://www.baidu.com')
print(res)
return n if __name__ == '__main__':
jobs = [gevent.spawn(target0, 1),gevent.spawn(target0, 2),gevent.spawn(target0, 3)]
gevent.joinall(jobs)
print([job.value for job in jobs])

gevent模块分析

  • gevent顶层方法
gevent.spawn():创建一个普通的Greenlet对象并切换;
gevent.spawn_later(seconds=3) # 延时创建一个普通的Greenlet对象并切换
gevent.spawn_raw() # 创建的协程对象属于一个组
gevent.getcurrent() # 返回当前正在执行的greenlet
gevent.joinall(jobs):将协程任务添加到事件循环,接收一个任务列表
gevent.wait() # 可以替代join函数等待循环结束,也可以传入协程对象列表
gevent.kill() # 杀死一个协程
gevent.killall() # 杀死一个协程列表里的所有协程
monkey.patch_all():非常重要,会自动将python的一些标准模块替换成gevent框架
  • 设置强制切换的时间
# 手动设置CPU密集型最大执行时间,如果是单线程的协程不需要关注这个
sys.setcheckinterval(n):每n条执行尝试进行线程切换,n必须是int
sys.getswitchinterval() # 默认5ms切换
  • Greenlet对象
# Greenlet对象
from gevent import Greenlet # Greenlet对象创建
job = Greenlet(target0, 3)
Greenlet.spawn() # 创建一个协程并启动
Greenlet.spawn_later(seconds=3) # 延时启动 # 协程启动
job.start() # 将协程加入循环并启动协程
job.start_later(3) # 延时启动 # 等待任务完成
job.join() # 等待任务完成
job.get() # 获取协程返回的值 # 任务中断和判断任务状态
job.dead() # 判断协程是否死亡
job.kill() # 杀死正在运行的协程并唤醒其他的协程,这个协程将不会再执行,可以
job.ready() # 任务完成返回一个真值
job.successful() # 任务成功完成返回真值,否则抛出错误 # 获取属性
job.loop # 时间循环对象
job.value # 获取返回的值 # 捕捉异常
job.exception # 如果运行有错误,获取它
job.exc_info # 错误的详细信息 # 设置回调函数
job.rawlink(back) # 普通回调,将job对象作为回调函数的参数
job.unlink() # 删除回调函数
# 执行成功的回调函数
job.link_value(back)
# 执行失败的回调函数
job.link_exception(back)

限制并发

  • 通过设置协程池Pool来限制运行的协程的最大数目。该Pool和multiprocessing进程模块的Pool的API十分相似。

  • Group类管理一组不限制数目的协程对象,但是Pool是它的子类,使用一般用Pool替代。

def tag():
print('--start---')
x = requests.get('http://www.baidu.com')
print(x)
print('------end--------')
return 0 if __name__ == '__main__': from gevent.pool import Pool
p = Pool(5)
for i in range(10):
p.apply_async(tag)
p.join()
  • gevent.Pool的特殊方法
pool.wait_available():等待直到有一个协程有结果
pool.dd(greenlet):向进程池添加一个方法并跟踪,非阻塞
pool.discard(greenlet):停止跟踪某个协程
pool.start(greenlet):加入并启动协程
pool.join():阻塞等待结束
pool.kill():杀死所有跟踪的协程
pool.killone(greenlet):杀死一个协程

python并发编程之gevent协程(四)的更多相关文章

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

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

  2. python并发编程之IO模型 (四十九)

    IO模型介绍 http://www.cnblogs.com/linhaifeng/articles/7454717.html

  3. python并发编程之Queue线程、进程、协程通信(五)

    单线程.多线程之间.进程之间.协程之间很多时候需要协同完成工作,这个时候它们需要进行通讯.或者说为了解耦,普遍采用Queue,生产消费模式. 系列文章 python并发编程之threading线程(一 ...

  4. python并发编程之multiprocessing进程(二)

    python的multiprocessing模块是用来创建多进程的,下面对multiprocessing总结一下使用记录. 系列文章 python并发编程之threading线程(一) python并 ...

  5. python并发编程之threading线程(一)

    进程是系统进行资源分配最小单元,线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.进程在执行过程中拥有独立的内存单元,而多个线程共享内存等资源. 系列文章 py ...

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

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

  7. Python核心技术与实战——十七|Python并发编程之Futures

    不论是哪一种语言,并发编程都是一项非常重要的技巧.比如我们上一章用的爬虫,就被广泛用在工业的各个领域.我们每天在各个网站.App上获取的新闻信息,很大一部分都是通过并发编程版本的爬虫获得的. 正确并合 ...

  8. Python并发编程——多线程与协程

    Pythpn并发编程--多线程与协程 目录 Pythpn并发编程--多线程与协程 1. 进程与线程 1.1 概念上 1.2 多进程与多线程--同时执行多个任务 2. 并发和并行 3. Python多线 ...

  9. 15.python并发编程(线程--进程--协程)

    一.进程:1.定义:进程最小的资源单位,本质就是一个程序在一个数据集上的一次动态执行(运行)的过程2.组成:进程一般由程序,数据集,进程控制三部分组成:(1)程序:用来描述进程要完成哪些功能以及如何完 ...

随机推荐

  1. jsp和js中获取项目路径名

    一.jspa.<%=request.getContextPath()%>//结果:/projectNameb.${pageContext.request.contextPath}//结果: ...

  2. BZOJ3574 HNOI2014抄卡组(哈希)

    容易发现通配符中间的部分可以任意匹配,会造成的无法匹配的仅仅是前后缀,前缀和后缀可以分别独立处理.如果字符串均有通配符,只需要按前/后缀长度排序然后暴力匹配就可以了. 问题在于存在无通配符的字符串.显 ...

  3. http://www.pythonchallenge.com/ 网站题解

    在知乎中无意发现了这个网站,做了几题发现挺有趣的,这里记录下自己的解题思路,顺便对比下答案中的思路 网页:http://www.pythonchallenge.com/ 目前只做了几题,解题的方法就是 ...

  4. VLC for Android 编译过程

    首先,给一个VLC的官网链接:VLC-AndroidCompile 上面有编译所需要安装的插件,环境变量的配置等等信息:虽然是英语,但也挺好理解,这里就不再详述:此文主要记录我在编译的过程中遇到的一些 ...

  5. Docker学习笔记五:Docker生成jenkins容器,支持Java Web项目持续集成、持续部署

    一.创建jenkins容器: 1.拉取jeknin镜像 sudo docker pull jenkins 2.创建一个jenkins目录 sudo mkdir /jenkins 3.在jenkins目 ...

  6. 【BZOJ4011】【HNOI2015】落忆枫音(动态规划)

    [BZOJ4011][HNOI2015]落忆枫音(动态规划) 题面 BZOJ 洛谷 Description 「恒逸,你相信灵魂的存在吗?」 郭恒逸和姚枫茜漫步在枫音乡的街道上.望着漫天飞舞的红枫,枫茜 ...

  7. 利用script和scriptlet moniker绕过脚本白名单限制

    没事儿看了一下subtee和enigma0x3今年在BSides Nashville 2017上的演讲,觉得这两个猥琐男简直不能再猥琐了 :-)其中有一个猥琐小技巧,又可以让我们好好hunting一番 ...

  8. luoguP5105 不强制在线的动态快速排序

    emm 可重集合没用用.直接变成不可重复集合 有若干个区间 每个区间形如[L,R] [L,R]计算的话,就是若干个连续奇数的和.拆位统计1的个数 平衡树维护 加入一个[L,R],把相交的区间合并.之后 ...

  9. [Wf2011]Chips Challenge

    两个条件都不太好处理 每行放置的个数实际很小,枚举最多放x 但还是不好放 考虑所有位置先都放上,然后删除最少使得合法 为了凑所有的位置都考虑到,把它当最大流 但是删除最少,所以最小费用 行列相关,左行 ...

  10. [CF1087D]Minimum Diameter Tree

    link 题目大意 有$n$个点的前边权为$0$的树,你要加入$S$边权总量,可以为分数,使得当前树的直径最小. 题目分析 题目过于毒瘤,导致于最后$1$个小时一直在做此题,没想到真的只是一个结论一样 ...