python 协程库 asyncio 使用

asyncio提供的框架以事件循环(event loop)为中心,程序开启一个无限的循环,程序会把一些函数注册到事件循环上。当满足事件发生的时候,调用相应的协程函数。

事件循环loop调用方法loop.run_until_complete(…)驱动传入的参数,它的参数是一个future或者协程。如果是协程,run_until_complete方法和wait函数一样,把协程包装进一个Task对象中。协程、future和任务(Task)都由yield from驱动,这正是run_until_complete方法对其参数所做的事情。

yield from foo句法能够防止阻塞,是因为当前协程(即包括yield from代码的委派生成器)暂停后,控制权回到事件循环手中,再去驱动其它协程; foo future或协程运行结束后,把结果返回到暂停的协程(yield from自动捕获StopIteration),将其恢复。这样,最外层的事件循环始终可以不被阻塞,并发地调度所有协程向前推进。

Future
future是一个数据结构,表示还未完成的工作结果。事件循环可以监视Future对象是否完成。从而允许应用的一部分等待另一部分完成一些工作。

Task
task是Future的一个子类,它知道如何包装和管理一个协程的执行。任务所需的资源可用时,事件循环会调度任务允许,并生成一个结果,从而可以由其他协程消费。

await
await 用于挂起阻塞的异步调用,用于挂起耗时的操作。
使用await可以针对耗时的操作进行挂起,就像生成器里的yield一样,函数让出控制权。协程遇到await,事件循环将会挂起该协程,执行await 后面跟着的那个协程,直到await来的协程也挂起或者执行完毕,再进行下一个协程的执行。

1、简单的创建一个协程

async关键字定义一个协程(coroutine),协程也是一种对象。协程不能直接运行,需要把协程加入到事件循环(loop),由后者在适当的时候调用协程。

asyncio.get_event_loop 方法可以创建一个事件循环。
然后使用 run_until_complete 将协程注册到事件循环,并启动事件循环。
下面是3.6版本的创建协程方法。3.7的更加方便了,可参考官方文档:asyncio

import asyncio


#@asyncio.coroutine    # 3.4 版本,用这个方法声明是协程的方式。
#def func(n):
async def func(n):     # 3.4之后,用 async 加在 def 前面,表示这是一个协程对象。
    print('hello world <{}>'.format(n))
    # r = yield from asyncio.sleep(n)    # 3.4 版本,调用方式。
    r = await asyncio.sleep(3)           # 3.4之后 用 await 调用 。
    print('hello again<{}>'.format(n))


loop = asyncio.get_event_loop()      # 获取消息循环 Eventloop 
loop.run_until_complete(func(2))     # 把协程对象注册到事件循环中。(只注册一个协程对象的方法。)

tasks = [func(4), func(5)]           #  注册多个协程对象的方法。
loop.run_until_complete(asyncio.wait(tasks)) 
loop.close()

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

2、创建一个task

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

import asyncio
import time


async def do_some_work(x):
    await asyncio.sleep(3)
    print('Waiting: ', x)
    return 'do_some_work 的返回值 2222。'

start = time.time()

coroutine = do_some_work('随便一个参数')

loop = asyncio.get_event_loop()

# task = asyncio.ensure_future(coroutine)	     #  利用asyncio.ensure_future 生成一个task对象。
task = loop.create_task(coroutine)   			 #  利用 loop.create_task() 生成一个task对象。
print('未注册到循环时的Task:{}'.format(task))

loop.run_until_complete(task)					#  task 对象注册到事件循环中。
print('注册到循环后的Task:{}'.format(task))
print('Task.result() 值:{}'.format(task.result()))    # result() 方法获取返回协程的return值。
print('总耗时: ', time.time() - start)

运行结果为:

未注册到循环时的Task:<Task pending coro=<do_some_work() running at F:/procedure/进程、线程/协程/test.py:5>>
Waiting:  随便一个参数
注册到循环后的Task:<Task finished coro=<do_some_work() done, defined at F:/procedure/进程、线程/协程/test.py:5> result=None>
Task.result() 值:do_some_work 的返回值 2222。
总耗时:  3.0001378059387207

上面的 协程还可以绑定一个 回调函数,如下:

import asyncio
import time


def call_back(f):
    print('协程执行完后的调用')
    print(f'传入的参数为:{f.result()}')


async def do_some_work(x):
    await asyncio.sleep(3)
    print('Waiting: ', x)
    return 'do_some_work 的返回值。'

start = time.time()

coroutine = do_some_work('随便一个参数')

loop = asyncio.get_event_loop()

task = asyncio.ensure_future(coroutine)
# task = loop.create_task(coroutine)

task.add_done_callback(call_back)    #  绑定回调函数,它会把  do_some_work(x) 最后的return 值当作参数 传给 call_back()

loop.run_until_complete(task)
print('总耗时: ', time.time() - start)

3 、协程状态

future对象有几个状态:

Pending
Running
Done
Cancelled

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

import itertools
import sys, asyncio


async def spin(msg):  	# 这个协程用来逐个显示 “ | / - \ ”,就像一条线在转圈圈。
    write, flush = sys.stdout.write, sys.stdout.flush    # 把标准的输入、输出赋值给变量 write, flush。
    for char in itertools.cycle('|/-\\'): 				 # itertools.cycle() 无限循环。
        status = char + ' ' + msg
        write(status)  									# 把 status写入到标准输出中,即可以在cmd里显示。
        flush()       								    # 刷新缓存,使其马上显示到屏幕上。
        write('\x08' * len(status))  					# \x08 退格符和\b等效,用来清空屏幕的输出显示。
        try:
            await asyncio.sleep(0.1)    				# 暂停 0.1 秒,让出本协程对线程的占用。
        except asyncio.CancelledError:
            break


async def slow_function(): 		    # 假设这是 处理 I/O事件的耗时计算。
    await asyncio.sleep(5)   		# 用协程的 asyncio.sleep() 让出 占用时间。
    return 2222


async def supervisor():     	 # 该协程函数用来创建一个可以注册到循环事件中的future对象。
    spinner = asyncio.ensure_future(spin('thinking……'))    # 把 spin() 协程函数生成 task 对象。
    print(f'spinner object: {spinner}')
    result = await slow_function()      # 用 await 来执行   slow_function() 这个协程。但这个协程是挂起5秒钟。
    spinner.cancel()    			    # 5秒后,向Task(协程)传入取消信号,会在 spin() 的 await 处抛出 asyncio.CancelledError 错误。
    return result


def main():
    loop = asyncio.get_event_loop() 		 # 获取消息循环 Eventloop
    result = loop.run_until_complete(supervisor())
    loop.close()
    print(f'Answer: {result}', flush=True)

if __name__ == '__main__':
    main()

文章部分内容参考自

协程与线程的区别在这里插入图片描述

在这里插入图片描述

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值