python并发框架 gevent_python并发编程之gevent协程(四)

协程的含义就不再提,在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):杀死一个协程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值