python协程实时输出_python协程

不知道你有没有被问到过有没有使用过的python协程?

协程是什么?

协程是一种用户态轻量级,是实现并发编程的一种方式。说到并发,就能想到了多线程 / 多进程模型,是解决并发问题的经典模型之一。

但是随刻客户端数量达到一定量级,进程上下文切换占用了大量的资源,线程也顶不住如此巨大的压力,对于多线程应用,CPU通过切片的方式来切换线程间的执行,

线程切换时需要耗时(保存状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。线程是抢占式的调度,而协程是协同式的调度,也就是说,协程需要自己做调度。

协程有什么用?

在别的语言中协程意义不大,多线程可解决问题,但是python因为他有GIL(Global Interpreter Lock 全局解释器锁 )在同一时间只有一个线程在工作,如果一个线程里面I/O操作特别多,协程就比较适用;

在python中多线程的执行情况如下图:

1008515-20200503165234659-1684798038.png

怎么实现协程?

python2.7中用生成器实现协程

在python3.7以后使用asyncio 和 async / await 的方法实现协程,实现过程就变得很简单

具体说明:简单的代码示例

import asyncio

import time

async def sub_function(str):

print(' {}'.format(str))

sleep_time = int(str.split('_')[-1])

await asyncio.sleep(sleep_time)

print('OK {}'.format(str))

async def main(strs):

for str in strs:

await sub_function(str)

# asyncio.run(main()) 作为主程序的入口函数,在程序运行周期内,只调用一次 asyncio.run

t0 = time.time()

asyncio.run(main(['str_1', 'str_2', 'str_3', 'str_4']))

t1 = time.time()

print("Total time running: %s seconds" %(str(t1 - t0)))

输出结果:一共是 10s 和我们顺序分析 分别等待 1 2 3 4 秒 好像没有什么提升作用,继续往下看

1008515-20200503171709159-1813141489.png

执行协程的方法有三种:

1. 入上例所示,用await 来调用实现,但是await 执行的效果,和 Python 正常执行是一样的,也就是说程序会阻塞在这里,进入被调用的协程函数,执行完毕返回后再继续,而这也是 await 的字面意思。

await 是同步调用,相当于我们用异步接口写了个同步代码,所以运行时间没有得到提升。

2. 上栗的异步想过没有体现出来,接下来我们使用另一个概念 task

async defsub_function(str):print('{}'.format(str))

sleep_time= int(str.split('_')[-1])

await asyncio.sleep(sleep_time)print('OK {}'.format(str))

asyncdefmain(strs):

#

tasks= [asyncio.create_task(sub_function(str)) for str instrs]for task intasks:

await task#asyncio.run(main()) 作为主程序的入口函数,在程序运行周期内,只调用一次 asyncio.run

t0 =time.time()

asyncio.run(main(['str_1', 'str_2', 'str_3', 'str_4']))

t1=time.time()print("Total time running: %s seconds" %(str(t1 - t0)))

们有了协程对象后,便可以通过 asyncio.create_task 来创建任务。任务创建后很快就会被调度执行,

这样,我们的代码也不会阻塞在任务这里。用for task in tasks: await task 即可。这次,你就看到效果了吧,结果显示,运行总时长等于运行时间最长一句。

运行结果:

1008515-20200503173357946-1684545096.png

3. 上面的task也可以用下面的方式写:

async defmain(strs):

tasks= [asyncio.create_task(sub_function(str)) for str instrs]

await asyncio.gather(*tasks)

唯一要注意的是,*tasks 解包列表,将列表变成了函数的参数;与之对应的是, ** dict 将字典变成了函数的参数。

在实际中,我们会遇到接口超时,我们就需要取消的情况,这种情况该怎么处理呢?再进一步,如果某些协程运行时出现错误,又该怎么处理呢?

importtimeimportasyncio

asyncdefworker_1():

await asyncio.sleep(1)return 1asyncdefworker_2():

await asyncio.sleep(2)return 2 /0

asyncdefworker_3():

await asyncio.sleep(3)return 3asyncdefmain():

task_1=asyncio.create_task(worker_1())

task_2=asyncio.create_task(worker_2())

task_3=asyncio.create_task(worker_3())

await asyncio.sleep(2)

task_3.cancel()

res= await asyncio.gather(task_1, task_2, task_3, return_exceptions=True)print(res)

t0=time.time()

asyncio.run(main())

t1=time.time()print("Total time running: %s seconds" %(str(t1 - t0)))

1008515-20200503181540044-1880333332.png

要注意return_exceptions=True这行代码。这个参数默认值为False, 如果不设置这个参数,错误就会完整地 throw 到我们这个执行层,从而需要 try except 来捕捉,

这也就意味着其他还没被执行的任务会被全部取消掉。为了避免这个局面,我们将 return_exceptions 设置为 True 即可。

1008515-20200503181749959-1226977486.png

线程能实现的,协程都能做到.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值