Published on

asyncio笔记

参考

asyncio

event_loop:事件循环,相当于一个无限循环,我们可以把一些函数注册到这个事件循环上,当满足条件发生的时候,就会调用对应的处理方法。

协程 corountine:async 定义的函数直接调用会返回一个 corountine对象,无法直接执行,需要注册到事件循环中才可以执行。

任务 task :对corountine对象进一步的封装,包含了任务的状态。asyncio.create_task()将一个协程对象封装成任务。将其包装成任务,任务是可以包含各种状态的,异步编程最重要的就是对异步操作状态的把控。

创建任务:task = asyncio.ensure_future(coro())
task = asyncio.create_task(coro())  # 这是3.7版本新添加的
也可以使用loop.create_future();loop.create_task(coro)
asyncio.all_tasks(loop=None) 返回某一个loop中还没有结束的任务

Future:它是一个“更底层”的概念,他代表一个一步操作的最终结果,因为一步操作一般用于耗时操作,结果不会立即得到,会在“将来”得到异步运行的结果,故而命名为Future。

当一个Future对象被等待的时候,协程会一直等待,直到Future已经运算完毕。

三者的关系, coroutine可以自动封装成task,而Task是Future的子类。

import asyncio

# 定义了一个简单的协程
async def simple_async():
    print('hello')
    await asyncio.sleep(1) # 休眠1秒
    print('python')

# 使用asynio中run方法运行一个协程
asyncio.run(simple_async())

# 执行结果为
hello
python
import asyncio

# 定义了一个简单的协程
async def simple_async():
    print('hello')
    await asyncio.sleep(1) # 休眠1秒
    print('python')

# ----asyncio.run() 等价于
loop = asyncio.get_event_loop()
loop.run_until_complete(simple_async())

# ----
loop = asyncio.get_event_loop()  # 1. 创建事件循环
# # task = asyncio.ensure_future(simple_async())
task = loop.create_task(simple_async()) # 将协程包装成任务列表
#  task.add_done_callback(callback)   # 并被任务绑定一个回调函获取结果
loop.run_until_complete(task)  # 将task添加到事件循环,通过事件循环运行
print(task.result())  # 通过result()获取结果

loop.close()
# ---
# task = asyncio.ensure_future(coroutine) # 没有声明 loop 也可以提前创建task 对象
tasks = [asyncio.ensure_future(coroutine()) for i in [*,*]]
loop = asyncio.get_event_loop()
tasks = asyncio.gather(*tasks) # 使用gather同时注册多个任务,实现并发
loop.run_until_complete(tasks)

在用协程的时候,CPU就不来分配时间了,时间由你们自己决定,你觉得干这件事情很耗时,要等IO啥的,你就干一会歇一会,等到等IO的时候就主动让出CPU,让别人上去干活,别人也是讲道理的,干一会也会把时间让给你。协程就是使用了这种思想,让编程者控制各个任务的运行顺序,从而最大可能的发挥CPU的性能。

import asyncio

async def eternity():
    print('我马上开始执行')
    await asyncio.sleep(3600)  #当前任务休眠1小时,即3600秒
    print('终于轮到我了')

async def main():
    # Wait for at most 1 second
    try:
        print('等你3秒钟哦')
        await asyncio.wait_for(eternity(), timeout=3)  #休息3秒钟了执行任务
    except asyncio.TimeoutError:
        print('超时了!')

asyncio.run(main())

'''运行结果为:
等你3秒钟哦
我马上开始执行
超时了!
'''

当异步操作需要执行的时间超过waitfor设置的timeout,就会触发异常,所以在编写程序的时候,如果要给异步操作设置timeout,一定要选择合适,如果异步操作本身的耗时较长,而你设置的timeout太短,会涉及到她还没做完,就抛出异常了。(7)多个协程函数时候的等候注意:该函数的返回值是两个Tasks/Futures的集合:参数解释:


import asyncio

async def cancel_me():
    print('cancel_me(): before sleep')
    try:
        await asyncio.sleep(3600) #模拟一个耗时任务
    except asyncio.CancelledError:
        print('cancel_me(): cancel sleep')
        raise
    finally:
        print('cancel_me(): after sleep')

async def main():
    #通过协程创建一个任务,需要注意的是,在创建任务的时候,就会跳入到异步开始执行
    #因为是3.7版本,创建一个任务就相当于是运行了异步函数cancel_me
    task = asyncio.create_task(cancel_me())
    #等待一秒钟
    await asyncio.sleep(1)
    print('main函数休息完了')
    #发出取消任务的请求
    task.cancel()
    try:
        await task  #因为任务被取消,触发了异常
    except asyncio.CancelledError:
        print("main(): cancel_me is cancelled now")

asyncio.run(main())

'''运行结果为:
cancel_me(): before sleep
main函数休息完了
cancel_me(): cancel sleep
cancel_me(): after sleep
main(): cancel_me is cancelled now
'''

asyncio.get_running_loop()

asyncio.get_event_loop()

asyncio.set_event_loop(loop)

asyncio.new_event_loop() loop.run_until_complete(future)。运行事件循环,直到future运行结束

loop.create_future(coroutine) ,返回future对象

loop.time() # 时间循环的时钟

freezing

各个异步方法之间不完全是独立


import asyncio
import time
import threading

#定义一个异步操作
async def hello1(a,b):
    print(f"异步函数开始执行")
    await asyncio.sleep(3)
    print("异步函数执行结束")
    return a+b

#在一个异步操作里面调用另一个异步操作
async def main():
    c=await hello1(10,20)
    print(c)
    print("主函数执行")

loop = asyncio.get_event_loop()
tasks = [main()]
loop.run_until_complete(asyncio.wait(tasks))

loop.close()

'''运行结果为:
异步函数开始执行(在此处要等待3秒)
异步函数执行结束
30
主函数执行
'''

future = asyncio.run_coroutine_threadsafe(coro_func(), loop) # 在新线程中运行协程

result = future.result() #等待获取Future的结果

yield

为什么yield可以实现协程:

当一个函数中有yield时,函数是生成器,调用代码不会立即执行,而是返回一个生成器对象。

返回一个值,接收调用者的参数。yield具有中断等待的功能。

GEN_CREATED:等待执行,即还没有进入协程

GEN_RUNNING:解释器执行(这个只有在使用多线程时才能查看到他的状态,而协程是单线程的)

GEN_SUSPENDED:在yield表达式处暂停(协程在暂停等待的时候的状态)

GEN_CLOSED:执行结束(协程执行结束了之后的状态)

版权声明:本文为CSDN博主「LoveMIss-Y」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_27825451/article/details/85234610

from inspect import getgeneratorstate  # 一定要导入
from time import sleep


def my_generator():
    for i in range(3):
        sleep(0.5)
        x = yield i + 1
        print(x)


g = my_generator()  # 创建一个生成器对象


def main(generator):
    try:
        print("生成器初始状态为:{0}".format(getgeneratorstate(g)))
        print(next(g))
        # 激活生成器
        print("生成器初始状态为:{0}".format(getgeneratorstate(g)))
        print(g.send(100))
        print("生成器初始状态为:{0}".format(getgeneratorstate(g)))
        print(next(g))
        print("生成器初始状态为:{0}".format(getgeneratorstate(g)))
        print(next(g))
    except StopIteration:
        print('全部迭代完毕了')
        print("生成器初始状态为:{0}".format(getgeneratorstate(g)))


main(g)
'''运行结果为:
生成器初始状态为:GEN_CREATED
1
生成器初始状态为:GEN_SUSPENDED
100
2
生成器初始状态为:GEN_SUSPENDED
None
3
生成器初始状态为:GEN_SUSPENDED
None
全部迭代完毕了
生成器初始状态为:GEN_CLOSED
'''

(1)协程函数的返回值不是特别方便获取,为什么参见上一篇文章,只能够通过出发StopIteration异常,然后通过该异常的value属性获取;

(2)Python的生成器是协程coroutine的一种形式,但它的局限性在于只能向它的直接调用者每次yield一个值。这意味着那些包含yield的代码不能想其他代码那样被分离出来放到一个单独的函数中。这也正是yield from要解决的。

yield from

如果将yield理解成“返回”,那么yield from就是“从什么(生成器)里面返回”

def my_generator():
    for i in range(5):
        if i==2:
            return '我被迫中断了'
        else:
            yield i

def main(generator):
    try:
        print(next(generator))   #每次迭代一个值,则会显式出发StopIteration
        print(next(generator))
        print(next(generator))
        print(next(generator))
        print(next(generator))
    except StopIteration as exc:
        print(exc.value)     #获取返回的值

g=my_generator()
main(g)
'''运行结果为:
0
1
我被迫中断了
'''

yield from让解释器可以捕获stopiteration异常,并且return返回的值或者是StopIteration的value 属性的值变成 yield from 表达式的值

# 子生成器
def my_generator():
    for i in range(5):
        if i==2:
            return '我被迫中断了'
        else:
            yield i

# 委派生成器
def wrap_my_generator(generator):  #定义一个包装“生成器”的生成器,它的本质还是生成器
  # 在使用yield from的时候,多了一个对原始my_generator的包装函数,然后调用方是通过这个包装函数(后面会讲到它专有的名词)来与生成器进行交互的,即“调用方——>生成器包装函数——>生成器函数(协程函数)”;
    result=yield from generator    #自动触发StopIteration异常,并且将return的返回值赋值给yield from表达式的结果,即result
    print(result)

# 调用方
def main(generator):
    for j in generator:
        print(j)

g=my_generator()
wrap_g=wrap_my_generator(g)
main(wrap_g)  #调用
'''运行结果为:
0
1
我被迫中断了
'''
def average():
    total = 0.0  #数字的总和
    count = 0    #数字的个数
    avg = None   #平均值
    while True:
        num = yield avg
        total += num
        count += 1
        avg = total/count

def wrap_average(generator):
    yield from generator

#定义一个函数,通过这个函数向average函数发送数值
def main(wrap):
    print(next(wrap))  #启动生成器
    print(wrap.send(10))  # 10
    print(wrap.send(20))  # 15
    print(wrap.send(30))  # 20
    print(wrap.send(40))  # 25

g = average()
wrap=wrap_average(g)
main(wrap)

'''运行结果为:
None
10.0
15.0
20.0
25.0
'''