Python学习篇29-协程和异步IO

本文围绕Python的协程和异步IO展开。介绍了协程概念、优缺点,以及通过yield和send实现协程的方法。还阐述了异步IO(asyncio),包括其编程模型、事件循环、协程、任务等概念,以及定义协程、创建任务、绑定回调等操作,最后提及协程停止的相关内容。

协程和异步IO

协程的概念

协程,又称微线程,纤程。英文名Coroutine,是一种用户态的轻量级线程。

子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B,B在执行过程中又调用了C,C执行完毕返回,B执行完毕返回,最后是A执行完毕。所以子程序调用是通过栈实现的,一个线程就是执行一个子程序。子程序调用总是一个入口,一次返回,调用顺序是明确的。而协程的调用和子程序不同。

线程是系统级别的它们由操作系统调度,而协程则是程序级别的由程序根据需要自己调度。在一个线程中会有很多函数,我们把这些函数称为子程序,在子程序执行过程中可以中断去执行别的子程序,而别的子程序也可以中断回来继续执行之前的子程序,这个过程就称为协程。也就是说在同一线程内一段代码在执行过程中会中断然后跳转执行别的代码,接着在之前中断的地方继续开始执行。

协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此:协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。

【示 例 】 代 码 描 述 协 程

def A():
	print('1')
	print('2')
	print('3')

def B():
	print('x')
	print('y')
	print('z')

由协程执行,在执行A的过程中,可以随时中断,去执行B,B也可能在执行过程中中断再去执行A,可能的结果是:

1
2
x
y
3
z

但是在A中是没有调用B的,所以协程的调用比函数调用理解起来要难一些,看起来A、B的执行有点像多线程,但协程的特点在于是一个线程执行,那和多线程比,协程有何优势?

协程的优点:

  • (1)无需线程上下文切换的开销,协程避免了无意义的调度,由此可以提高性能(但

也因此,程序员必须自己承担调度的责任,同时,协程也失去了标准线程使用多CPU的能力)

  • (2)无需原子操作锁定及同步的开销
  • (3)方便切换控制流,简化编程模型
  • (4)高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合

用于高并发处理。

协程的缺点:

  • (1)无法利用多核资源:协程的本质是个单线程,它不能同时将单个CPU的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。

  • (2)进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序。

yield 的 使 用

​ Python 对 协 程 的 支 持 是 通 过 generator 实 现 的 。 在 generator 中 , 不 但 可 以 通 过 for 循 环 来 迭 代 , 还 可 以 不 断 调 用 next()函 数 获 取 由 yield 语 句 返 回 的 下 一 个 值 。

​ 先 把 yield 看 做 “ return” , 这 个 是 直 观 的 , 它 首 先 是 个 return, 普 通 的 return 是 什 么 意 思 ,就 是 在 程 序 中 返 回 某 个 值 ,返 回 之 后 程 序 就 不 再 往 下 运 行 了 。看 做 return 之 后 再 把 它 看 做 一 个 是 生 成 器 ( generator) 的 一 部 分 ( 带 yield 的 函 数 才 是 真 正 的 迭 代 器 ) 。

【示 例 】 yield 的 使 用

def foo():
    print("starting...")
    while True:
        res = yield 4
        print("res:",res)

g = foo()
print(next(g))
print("*"*20)
print(next(g))

执 行 结 果 :

starting...
4
********************
res: None
4

执行过程:

1.程序开始执行以后,因为foo函数中有yield关键字,所以foo函数并不会真的执行,而是先得到一个生成器g(相当于一个对象)

2.直到调用next方法,foo函数正式开始执行,先执行foo函数中的print方法,然后进入while循环

3.程序遇到yield关键字,然后把yield想成return,return了一个4之后,程序停止,并没有执行赋值给res操作,此时next(g)语句执行完成,所以输出的前两行(第一个是while上面的print的结果,第二个是return出的结果)是执行print(next(g))的结果,

4.程序执行print(“*”*20),输出20个*

5.又开始执行下面的print(next(g)),这个时候和上面那个差不多,不过不同的是,这个时候是从刚才那个next程序停止的地方开始执行的,也就是要执行res的赋值操作,这时候要注意,这个时候赋值操作的右边是没有值的(因为刚才那个是return出去了,并没有给赋值操作的左边传参数),所以这个时候res赋值是None,所以接着下面的输出就是res:None,

6.程序会继续在while里执行,又一次碰到yield,这个时候同样return出4,然后程序停止,print函数输出的4就是这次return出的4。

带yield的函数是一个生成器,而不是一个函数了,这个生成器有一个函数就是next函数,next就相当于“下一步”生成哪个数,这一次的next开始的地方是接着上一次的next停止的地方执行的,所以调用next的时候,生成器并不会从foo函数的开始执行,只是接着上一步停止的地方开始,然后遇到yield后,return出要生成的数,此步就结束。

【示 例 】 yield 简 单 实 现 协 程

import time

def A():
    while True:
        print('----A----')
        yield
        time.sleep(0.5)

def B(c):
    while True:
    print('----B----') 
    c.__next__() 
    time.sleep(0.5)

if __name__ == '__main__':
    a=A()
    B(a)

执 行 结 果 :

----B----

----A----

----B----

----A----

----B----

----A----

----B----

----省 略 ----

send 发 送 数 据

send 是 发 送 一 个 参 数 给 res 的 , 因 为 上 面 讲 到 , return 的 时 候 , 并 没 有 把 4 赋 值 给 res, 下 次 执 行 的 时 候 只 好 继 续 执 行 赋 值 操 作 ,只 好 赋 值 为 None 了 ,而 如 果 用 send 的 话 ,开 始 执 行 的 时 候 ,先 接 着 上 一 次( return 4 之 后 )执 行 ,先 把 10 赋 值 给 了 res,然 后 执 行 next 的 作 用 , 遇 见 下 一 回 的 yield, return 出 结 果 后 结 束 。

【示 例 】 yieldsend 函 数 的 使 用

def foo():
    print("starting...")
    while True:
        res = yield 4
        print("res:",res)

g = foo()
print(next(g))
print("*"*20)
print(g.send(10))

执 行 结 果 :

starting…

4

********************

res: 10

4

【示 例 】 协 程 实 现 生 产 者 消 费 者

import time

# 生产者
def produce(c):
    c.send(None)
    for i in range(1,6):
        print('生产者生产%d产品 ' % i)
        c.send(str(i))
        time.sleep(1)
        
# 消费者  
def customer():
    res = ''
    while True:
        data = yield res
        if not data:
            return
        print('消费者消费%s产品 ' % data)
        
if __name__ == '__main__':
    c = customer()
    produce(c)

执 行 结 果 :

生产者生产1产品
消费者消费1产品
生产者生产2产品
消费者消费2产品
生产者生产3产品
消费者消费3产品
生产者生产4产品
消费者消费4产品
生产者生产5产品
消费者消费5产品

异 步 IOasyncio) 协 程

使用异步IO,无非是提高我们写的软件系统的并发。这个软件系统,可以是网络爬虫,也可以是Web服务等等。

并发的方式有多种,多线程,多进程,异步IO等。多线程和多进程更多应用于CPU密集型的场景,比如科学计算的时间都耗费在CPU上,利用多核CPU来分担计算任务。多线程和多进程之间的场景切换和通讯代价很高,不适合IO密集型的场景。而异步IO就是非常适合IO密集型的场景,比如网络爬虫和Web服务。

IO就是读写磁盘、读写网络的操作,这种读写速度比读写内存、CPU缓存慢得多,前者的耗时是后者的成千上万倍甚至更多。这就导致,IO密集型的场景99%以上的时间都花费在IO等待的时间上。异步IO就是把CPU从漫长的等待中解放出来的方法。

asyncio

asyncio是Python3.4版本引入的标准库,直接内置了对异步IO的支持。asyncio的编程模型就是一个消息循环。我们从asyncio模块中直接获取一个EventLoop的引用,然后把需要执行的协程扔到EventLoop中执行,就实现了异步IO。

  • (1)event_loop事件循环:程序开启一个无限的循环,程序员会把一些函数注册到事

件循环上。当满足事件发生的时候,调用相应的协程函数。

  • (2)coroutine协程:协程对象,指一个使用async关键字定义的函数,它的调用不会立即执行函数,而是会返回一个协程对象。协程对象需要注册到事件循环,由事件循环调用。

  • (3)task任务:一个协程对象就是一个原生可以挂起的函数,任务则是对协程进一步封装,其中包含任务的各种状态。

  • (4)future:代表将来执行或没有执行的任务的结果。它和task上没有本质的区别

  • (5)async/await关键字:python3.5用于定义协程的关键字,async定义一个协程,await用于挂起阻塞的异步调用接口。

定义一个协程

定 义 一 个 协 程 很 简 单 , 使 用 async 关 键 字 , 就 像 定 义 普 通 函 数 一 样 :

【示 例 】 定 义 一 个 协 程

import time,asyncio

now = lambda : time.time()
#通 过 aysnc 定 义 一 个 协 程 , 该 协 程 不 能 直 接 运 行 , 需 要 将 协 程 加 入 到 事 件 循 环 中 
async def do_work(x):
	print('waiting:%d'%x)

start = now()
#得 到 一 个 协 程 对 象 
coroutine=do_work(2)

#创 建 一 个 事 件 循 环 
loop=asyncio.get_event_loop()

#将 协 程 对 象 加 入 到 事 件 循 环 中 
loop.run_until_complete(coroutine) 
print('TIME: ', now() - start)

执 行 结 果 :

在这里插入图片描述

通 过 async 关 键 字 定 义 一 个 协 程( coroutine),协 程 也 是 一 种 对 象 。协 程 不 能 直 接 运 行 , 需 要 把 协 程 加 入 到 事 件 循 环( loop),由 后 者 在 适 当 的 时 候 调 用 协 程 。asyncio.get_event_loop 方 法 可 以 创 建 一 个 事 件 循 环 , 然 后 使 用 run_until_complete 将 协 程 注 册 到 事 件 循 环 , 并 启 动 事 件 循 环 。

创建一个task

协 程 对 象 不 能 直 接 运 行 , 在 注 册 事 件 循 环 的 时 候 , 其 实 是 run_until_complete 方 法 将 协 程 包 装 成 为 了 一 个 任 务 ( task)对 象 。所 谓 task 对 象 是 future 类 的 子 类 。保 存 了 协 程 运 行 后 的 状 态 , 用 于 未 来 获 取 协 程 的 结 果 。

asyncio.ensure_future(coroutine) 和 loop.create_task(coroutine)都 可 以 创 建 一 个 task, run_until_complete 的 参 数 是 一 个 futrue 对 象 。 当 传 入 一 个 协 程 , 其 内 部 会 自 动 封 装 成 task, task 是 future 的 子 类 。 isinstance(task, asyncio.Future)将 会 输 出 True。

【示 例 】 创 建 一 个 task

import asyncio
import time
now = lambda: time.time()
async def do_work(x):
	print('Waiting: ', x)
start = now()
coroutine = do_work(2)
loop = asyncio.get_event_loop()
#创建一个task
task = loop.create_task(coroutine)
#task=asyncio.ensure_future(coroutine)
print(task)
loop.run_until_complete(task)
print(task)
print('TIME: ', now() - start)

执 行 结 果 :
在这里插入图片描述

创 建 task 后 ,task 在 加 入 事 件 循 环 之 前 是 pending 状 态 ,因 为 do_work 中 没 有 耗 时 的 阻 塞 操 作 , task 很 快 就 执 行 完 毕 了 。 后 面 打 印 的 finished 状 态 。

绑定回调

​ 绑 定 回 调 ,在 task 执 行 完 毕 的 时 候 可 以 获 取 执 行 的 结 果 ,回 调 的 最 后 一 个 参 数 是 future 对 象 , 通 过 该 对 象 可 以 获 取 协 程 返 回 值 。 如 果 回 调 需 要 多 个 参 数 , 可 以 通 过 偏 函 数 导 入 。

【示 例 】 绑 定 回 调

import time
import asyncio

now = lambda: time.time()
async def do_some_work(x):
	print('Waiting: ', x)
	return 'Done after {}s'.format(x)

def callback(future):
	print('Callback: ', future.result())

start = now()
coroutine = do_some_work(2)
loop = asyncio.get_event_loop()![ref4]
task = asyncio.ensure_future(coroutine) task.add_done_callback(callback) loop.run_until_complete(task) print('TIME: ', now() - start)

执 行 结 果 :
在这里插入图片描述

从 上 面 实 例 可 以 看 到 ,coroutine 执 行 结 束 时 候 会 调 用 回 调 函 数 。并 通 过 参 数 future 获 取 协 程 执 行 的 结 果 。 创 建 的 task 和 回 调 里 的 future 对 象 , 实 际 上 是 同 一 个 对 象 。

futureresult

回 调 一 直 是 很 多 异 步 编 程 的 恶 梦 ,程 序 员 更 喜 欢 使 用 同 步 的 编 写 方 式 写 异 步 代 码 ,以 避 免 回 调 的 恶 梦 。回 调 中 我 们 使 用 了 future 对 象 的 result 方 法 。前 面 不 绑 定 回 调 的 例 子 中 ,可 以 看 到 task 有 fiinished 状 态 。 在 那 个 时 候 , 可 以 直 接 读 取 task 的 result 方 法 。

【示 例 】 直 接 读 取 taskresult 方 法

import time
import asyncio
now = lambda: time.time()
async def do_some_work(x):
    print('Waiting {}'.format(x))
    return 'Done after {}s'.format(x)
start = now()
coroutine = do_some_work(2)
loop = asyncio.get_event_loop()
task = asyncio.ensure_future(coroutine)
loop.run_until_complete(task)
print('Task ret: {}'.format(task.result()))
print('TIME: {}'.format(now() - start))

执 行 结 果 :
在这里插入图片描述

阻塞和await

使 用 async 可 以 定 义 协 程 对 象 , 使 用 await 可 以 针 对 耗 时 的 操 作 进 行 挂 起 , 就 像 生 成 器 里 的 yield 一 样 ,函 数 让 出 控 制 权 。协 程 遇 到 await,事 件 循 环 将 会 挂 起 该 协 程 ,执 行 别 的 协 程 , 直 到 其 他 的 协 程 也 挂 起 或 者 执 行 完 毕 , 再 进 行 下 一 个 协 程 的 执 行 。

耗 时 的 操 作 一 般 是 一 些 IO 操 作 , 例 如 网 络 请 求 , 文 件 读 取 等 。 我 们 使 用 asyncio.sleep 函 数 来 模 拟 IO 操 作 。 协 程 的 目 的 也 是 让 这 些 IO 操 作 异 步 化 。

【示 例 】 asyncio.sleep 函 数 来 模 拟 IO 操 作

import asyncio
import time
now = lambda: time.time()
async def do_some_work(x):
    print('Waiting: ', x)
    await asyncio.sleep(x)
    return 'Done after {}s'.format(x)
start = now()
coroutine = do_some_work(2)
loop = asyncio.get_event_loop()
task = asyncio.ensure_future(coroutine)
loop.run_until_complete(task)
print('Task ret: ', task.result())
print('TIME: ', now() - start)

执 行 结 果 :
在这里插入图片描述

并发和并行

并 发 和 并 行 一 直 是 容 易 混 淆 的 概 念 。并 发 通 常 指 有 多 个 任 务 需 要 同 时 进 行 ,并 行 则 是 同 一 时 刻 有 多 个 任 务 执 行 。用 上 课 来 举 例 就 是 ,并 发 情 况 下 是 一 个 老 师 在 同 一 时 间 段 辅 助 不 同 的 人 功 课 。并 行 则 是 好 几 个 老 师 分 别 同 时 辅 助 多 个 学 生 功 课 。简 而 言 之 就 是 一 个 人 同 时 吃 三 个 馒 头 还 是 三 个 人 同 时 分 别 吃 一 个 的 情 况 , 吃 一 个 馒 头 算 一 个 任 务 。

asyncio 实 现 并 发 ,就 需 要 多 个 协 程 来 完 成 任 务 ,每 当 有 任 务 阻 塞 的 时 候 就 await,然 后 其 他 协 程 继 续 工 作 。 创 建 多 个 协 程 的 列 表 , 然 后 将 这 些 协 程 注 册 到 事 件 循 环 中 。

【示 例 】 asyncio 实 现 并 发

import asyncio
import time
now = lambda: time.time()
async def do_some_work(x):
    print('Waiting: ', x)
    await asyncio.sleep(x)
    return 'Done after {}s'.format(x)
start = now()
coroutine1 = do_some_work(1)
coroutine2 = do_some_work(2)
coroutine3 = do_some_work(4)
tasks = [
    asyncio.ensure_future(coroutine1),
    asyncio.ensure_future(coroutine2),
    asyncio.ensure_future(coroutine3)
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
for task in tasks:
    print('Task ret: ', task.result())
    
print('TIME: ', now() - start)

执 行 结 果 :
在这里插入图片描述

总时间为4s左右。4s的阻塞时间,足够前面两个协程执行完毕。如果是同步顺序的任务,那么至少需要7s。此时我们使用了aysncio实现了并发。asyncio.wait(tasks)也可以使用asyncio.gather(*tasks),前者接受一个task列表,后者接收一堆task。

协程嵌套

使用async可以定义协程,协程用于耗时的io操作,也可以封装更多的io操作过程,这样就实现了嵌套的协程,即一个协程中await了另外一个协程,如此连接起来。

import asyncio
import time
now = lambda: time.time()
async def do_some_work(x):
    print('Waiting: ', x)
    await asyncio.sleep(x)
    return 'Done after {}s'.format(x)
async def main():
    coroutine1 = do_some_work(1)
    coroutine2 = do_some_work(2)
    coroutine3 = do_some_work(4)
    tasks = [
        asyncio.ensure_future(coroutine1),
        asyncio.ensure_future(coroutine2),
        asyncio.ensure_future(coroutine3)
    ]
    dones, pendings = await asyncio.wait(tasks)
    for task in dones:
        print('Task ret: ', task.result())
start = now()
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
print('TIME: ', now() - start)

执 行 结 果 :
在这里插入图片描述

如 果 使 用 的 是 asyncio.gather 创 建 协 程 对 象 , 那 么 await 的 返 回 值 就 是 协 程 运 行 的 结 果 。

【示 例 】 asyncio.gather 创 建 协 程 对 象

import asyncio
import time

now = lambda: time.time()
async def do_some_work(x):
    print('Waiting: ', x)
    await asyncio.sleep(x)
    return 'Done after {}s'.format(x)
async def main():
    coroutine1 = do_some_work(1)
    coroutine2 = do_some_work(2)
    coroutine3 = do_some_work(4)

    tasks = [
        asyncio.ensure_future(coroutine1),
        asyncio.ensure_future(coroutine2),
        asyncio.ensure_future(coroutine3)
    ]

    results = await asyncio.gather(*tasks)
    for result in results: 
        print('Task ret:![ref6] ', result)
start = now()
loop = asyncio.get_event_loop() 
loop.run_until_complete(main()) 
print('TIME: ', now() - start)

执 行 结 果 :
在这里插入图片描述

不在main协程函数里处理结果,直接返回await的内容,那么最外层的run_until_complete将会返回main协程的结果。

【示 例 】 不 在 main 协 程 函 数 里 处 理 结 果

import asyncio
import time

now = lambda: time.time()

async def do_some_work(x):
    print('Waiting: ', x)  
    await asyncio.sleep(x)
    return 'Done after {}s'.format(x)

async def main():
    coroutine1 = do_some_work(1)
    coroutine2 = do_some_work(2)
    coroutine3 = do_some_work(4)
    
    tasks = [
        asyncio.ensure_future(coroutine1),
        asyncio.ensure_future(coroutine2),
        asyncio.ensure_future(coroutine3)
    ]
    
    results = await asyncio.gather(*tasks)

    for result in results:
        print('Task ret: ', result) 

start = now()

loop = asyncio.get_event_loop()
loop.run_until_complete(main())

print('TIME: ', now() - start)

执 行 结 果 :
在这里插入图片描述

或 者 返 回 使 用 asyncio.wait 方 式 挂 起 协 程 。

【示 例 】 使 用 asyncio.wait 方 式 挂 起 协 程 import asyncio

import time
import asyncio

now = lambda: time.time()

async def do_some_work(x):
    print('Waiting: ', x)
    await asyncio.sleep(x)
    return 'Done after {}s'.format(x)

async def main():
    coroutine1 = do_some_work(1)
    coroutine2 = do_some_work(2) 
    coroutine3 = do_some_work(4)

    tasks = [
        asyncio.ensure_future(coroutine1),
        asyncio.ensure_future(coroutine2),
        asyncio.ensure_future(coroutine3)
    ]
    
    return await asyncio.wait(tasks)

start = now()

loop = asyncio.get_event_loop()
done, pending = loop.run_until_complete(main())

for task in done:
    print('Task ret: ', task.result())

执 行 结 果 :
在这里插入图片描述

【示 例 】 使 用 asyncioas_completed 方 法

import asyncio
import time

now = lambda: time.time()

async def do_some_work(x):
    print('Waiting: ', x)
    await asyncio.sleep(x)
    return 'Done after {}s'.format(x)

async def main():
    coroutine1 = do_some_work(1)
    coroutine2 = do_some_work(2)
    coroutine3 = do_some_work(4)
    
    tasks = [
        asyncio.ensure_future(coroutine1),
        asyncio.ensure_future(coroutine2),
        asyncio.ensure_future(coroutine3)
    ]

    for task in asyncio.as_completed(tasks):
        result = await task
        print('Task ret: {}'.format(result))

    start = now()
    
    loop = asyncio.get_event_loop()
    done = loop.run_until_complete(main())
    
    print('TIME: ', now() - start)

执 行 结 果 :
在这里插入图片描述

协程停止

上 面 见 识 了 协 程 的 几 种 常 用 的 用 法 ,都 是 协 程 围 绕 着 事 件 循 环 进 行 的 操 作 。future 对 象 有 几 个 状 态 :

  • 1) Pending
  • 2) Running
  • 3) Done
  • 4) Cancelled

创 建 future 的 时 候 , task 为 pending, 事 件 循 环 调 用 执 行 的 时 候 当 然 就 是 running, 调 用 完 毕 自 然 就 是 done,如 果 需 要 停 止 事 件 循 环 ,就 需 要 先 把 task 取 消 。可 以 使 用 asyncio.Task 获 取 事 件 循 环 的 task。

【示 例 】 协 程 停 止

import asyncio
import time

now = lambda: time.time()

async def do_some_work(x):
    print('Waiting: ', x)
    await asyncio.sleep(x)
    return 'Done after {}s'.format(x)

coroutine1 = do_some_work(1)
coroutine2 = do_some_work(2)
coroutine3 = do_some_work(2)

tasks = [
    asyncio.ensure_future(coroutine1),
    asyncio.ensure_future(coroutine2), 
    asyncio.ensure_future(coroutine3)
]

start = now()

loop = asyncio.get_event_loop()

try:
    loop.run_until_complete(asyncio.wait(tasks))
except KeyboardInterrupt as e:
    print(asyncio.Task.all_tasks())
    for task in asyncio.Task.all_tasks():
        print(task.cancel())
    loop.stop()
    loop.run_forever()
finally:
    loop.close()
    
print('TIME: ', now() - start)

启 动 事 件 循 环 之 后 , 马 上 ctrl+c , 会 触 发 run_until_complete 的 执 行 异 常 KeyBorardInterrupt。 然 后 通 过 循 环 asyncio.Task 取 消 future。 可 以 看 到 输 出 如 下 :
在这里插入图片描述

True 表 示 cannel 成 功 , loop stop 之 后 还 需 要 再 次 开 启 事 件 循 环 , 最 后 在 close, 不 然 还 会 抛 出 异 常 。

循 环 task, 逐 个 cancel 是 一 种 方 案 , 可 是 正 如 上 面 我 们 把 task 的 列 表 封 装 在 main 函 数 中 , main 函 数 外 进 行 事 件 循 环 的 调 用 。 这 个 时 候 , main 相 当 于 最 外 出 的 一 个 task, 那 么 处 理 包 装 的 main 函 数 即 可 。

【示 例 】 把 task 的 列 表 封 装 在 main 函 数 中 , 协 程 停 止

import asyncio
import time

now = lambda: time.time()

async def do_some_work(x):
    print('Waiting: ', x)
    await asyncio.sleep(x)
    return 'Done after {}s'.format(x)

async def main():
    
    coroutine1 = do_some_work(1)
    coroutine2 = do_some_work(2)
    coroutine3 = do_some_work(2)

    tasks = [
        asyncio.ensure_future(coroutine1),
        asyncio.ensure_future(coroutine2),
        asyncio.ensure_future(coroutine3)
    ]

    done, pending = await asyncio.wait(tasks)

    for task in done:
        print('Task ret: ', task.result())

    start = now()
    
    loop = asyncio.get_event_loop()
    
    task = asyncio.ensure_future(main())

    try:
        loop.run_until_complete(task)
    except KeyboardInterrupt as e:
        print(asyncio.Task.all_tasks())
        print(asyncio.gather(*asyncio.Task.all_tasks()).cancel())
        loop.stop()
        loop.run_forever()
    finally:
        loop.close()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值