学习一下asyncio

简述

asyncio即异步IO,是python3.7的一个亮点。在IO密集的场景下,对我们的编程非常有帮助,切记IO密集的场景而不是计算密集的场景。
asyncio的API分为高等和低等。一般情况下,我们使用高级API即可,当高级API不能满足的时候再使用低级API。下文中也主要讲述高级API。
献上官网地址:https://docs.python.org/3/library/asyncio.html
在这里插入图片描述

来个hello world, 进入异步IO的世界
"""
@author: hananmin
@time: 2019/12/14 12:15
"""
import asyncio
import time

async def say_after(delay, what):
    await asyncio.sleep(delay)
    print(what)


async def main():
    task1 = asyncio.create_task(
        say_after(1, 'hello'))

    task2 = asyncio.create_task(
        say_after(2, 'world'))

    print(f"started at {time.strftime('%X')}")

    # Wait until both tasks are completed (should take
    # around 2 seconds.)
    await task1
    await task2

    print(f"finished at {time.strftime('%X')}")


if __name__ == "__main__":
    asyncio.run(main())

代码输出如下图,嗯,没啥特殊的呀
在这里插入图片描述
请把task1的say_after的第一个入参改为3,即 say_after(3, “hello”) , 运行一下:
在这里插入图片描述
就问神奇不?? 这就是异步的效果,task1和task2都因为sleep导致阻塞,但是task2先睡醒,所以 “world” 先输出

async 和 await

async 装饰的函数,代表这个函数是协程(Coroutines)。await装饰的表达式,代表这个表达式有可能发生阻塞,如果出现阻塞,该await所在的协程就会暂停运行,让其他协程运行。
可以被await装饰的对象:

  • Coroutines
  • tasks,当多个协程需要并发执行的时候,需要把协程转化为task,下文会讲到
  • Futures,这是个低级的对象(low-level),一般不会用到,代表着异步操作的最终结果,通过他可以判断异步任务是否在执行
并发执行

不知道你有没有发现,hello world其实就是并发运行的,开始和结束的时间间隔由最长的任务决定。我们举一个不是并行的例子,也就是不使用tasks:

"""
@author: hananmin
@time: 2019/12/14 12:15
"""
import asyncio
import time

async def say_after(delay, what):
    await asyncio.sleep(delay)
    print(what)


async def main():
    # task1 = asyncio.create_task(
    #     say_after(3, 'hello'))
    #
    # task2 = asyncio.create_task(
    #     say_after(2, 'world'))

    print(f"started at {time.strftime('%X')}")

    # Wait until both tasks are completed (should take
    # around 2 seconds.)
    await  say_after(1, 'hello')
    await  say_after(2, 'world')

    print(f"finished at {time.strftime('%X')}")


if __name__ == "__main__":
    asyncio.run(main())

运行结果如下,我们看出没有使用task的情况,是不可并发执行的
在这里插入图片描述

  • 创建task

asyncio.create_task(coro, *, name=None)
入参是协程

  • 并发运行task

asyncio.gather(*aws, loop=None, return_exceptions=False)
入参可以是协程,这样会自动被转化为task运行。该方法也是可以被await装饰的。

  • timeout
    如果想给异步任务添加一个超时时间,那么可以使用wait_for()方法,看一个例子:
import asyncio
import datetime


async def eternity():
    # Sleep for one hour
    await asyncio.sleep(2)
    print('yay!')


async def main():
    # Wait for at most 1 second
    try:
        await asyncio.wait_for(eternity(), timeout=1.0)
    except asyncio.TimeoutError:
        print('timeout!')


if __name__ == "__main__":
    asyncio.run(main())

运行结果如下,说好的让你睡1秒,你却睡了两秒,那么我们只能无情的把你锤醒了:
在这里插入图片描述

  • asyncio.wait(aws, *, loop=None, timeout=None, return_when=ALL_COMPLETED)
    这个方法也是等待任务,但是和wait_for有些区别:
    (1)首先wait接收的是一个集合,而wait_for接收的是一个可等待对象
    (2)return_when可以设置返回的条件(FIRST_COMPLETED:只要有一个任务异常或者完成就返回;FIRST_EXCEPTION:有一个异常发生就返回;ALL_COMPLETED等待所有任务完成)
    (3)wait方法超时的时候不会抛出asyncio.TimeoutError异常,也不会调用task的cancel()方法

  • asyncio.as_completed(aws, *, loop=None, timeout=None)
    等待所有的异步动作完成,可以设置超时时间,超时就会抛出asyncio.TimeoutError

  • asyncio.run_coroutine_threadsafe(coro, loop)
    当你从一个线程调用,另一个线程中的协程的时候,请使用这个方法,这个方法是线程安全的

同步问题

当多个协程访问共享资源的时候,就需要锁这个东西。asyncio提供了几种锁,但是这些锁不是线程安全的,所以不可以跨越线程使用,下面我们来看看这几种锁

  • Lock
    当某个协程给一个资源加锁了,其他的协程就只能等待。
    在这里插入图片描述
    上一段使用锁的代码:
import asyncio


# 穷人
async def poor(lock: asyncio.Lock, name, money):
    print(f"Im poor {name}, i need money!!!")
    await lock.acquire()
    try:
        print(f'Yes!!! i get {money["value"]}')
        if money['value'] > 0:
            money['value'] = 0
    finally:
        lock.release()


async def main():
    # 钱
    money = {"value": 10}
    # 锁
    lock_obj = asyncio.Lock()
    # 开始抢钱
    done = asyncio.as_completed({poor(lock_obj, "XM", money), poor(lock_obj, "XH", money)})
    for f in done:
        await f

if __name__ == "__main__":
    asyncio.run(main())

程序输出:
在这里插入图片描述

  • Event
    当Event是unset状态的时候,允许多个协程等待他变为set状态。当它被设置为set状态的时候,等待的协程都会收到通知。
    在这里插入图片描述
    来一段代码:
import asyncio


# 有钱人
async def rich(event: asyncio.Event, name):
    print(f"Im rich {name}, i am coming!!!")
    await asyncio.sleep(2)
    # 发钱
    print(f'Yes!!! distribute money')
    event.set()
    await asyncio.sleep(3)
    # 停止发钱
    print(f'Fuck!!! Stop!!!')
    event.clear()


# 穷人
async def poor(event: asyncio.Event, name):
    print(f"Im poor {name}, i need money!!!")
    await event.wait()
    while event.is_set():
        print(f'{name}: Yes!!! i get money')
        await asyncio.sleep(1)


async def main():
    # 锁
    event_obj = asyncio.Event()
    # 开始抢钱
    done = asyncio.as_completed({rich(event_obj, "han"), poor(event_obj, "XM"), poor(event_obj, "XH")})
    for f in done:
        await f

if __name__ == "__main__":
    asyncio.run(main())

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

  • Condition
    这个比较强,是Event和Lock的结合。协程必须获取锁才能等待被通知,同样,只有获取锁才能通知其他协程。当协程接收到通知,就可以做其他的事情了。

在这里插入图片描述
上一段代码:

import asyncio
import datetime


# 有钱人
async def rich(condition: asyncio.Condition, name):
    print(f"{str(datetime.datetime.now())} Im rich {name}, i am coming!!!")
    await asyncio.sleep(1)
    await condition.acquire()
    # 发钱
    try:
        print(f'{str(datetime.datetime.now())} Hi!!! Come in')
    finally:
        condition.release()
    await asyncio.sleep(1)
    await condition.acquire()
    try:
        condition.notify(1)
        await asyncio.sleep(1)
        condition.notify(1)
    finally:
        condition.release()


# 穷人
async def poor(condition: asyncio.Condition, name):
    print(f"{str(datetime.datetime.now())} Im poor {name}, i need money!!!")
    await asyncio.sleep(1.5)
    await condition.acquire()
    try:
        print(f"{name}: {str(datetime.datetime.now())} Im coming!!!")
        count = 0
        await condition.wait()
        while count < 3:
            print(f'{name}: {str(datetime.datetime.now())} Yes!!! i get money')
            count = count + 1
    finally:
        condition.release()


async def main():
    # 锁
    condition_obj = asyncio.Condition()
    # 开始抢钱
    await asyncio.wait({rich(condition_obj, "han"), poor(condition_obj, "XM"), poor(condition_obj, "XH"),
                                 poor(condition_obj, "XZ")}, timeout=5)


if __name__ == "__main__":
    asyncio.run(main())

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

  • Semaphore
    资源数量是固定,每当协程访问资源时,数量就减少一个,访问完毕后就释放资源(资源数量加一);而当数量被消耗完后,其他的协程就只能等待了。
    在这里插入图片描述
    上一段代码:
import asyncio
import datetime


# 穷人
async def poor(sem: asyncio.Semaphore, name):
    print(f"{str(datetime.datetime.now())} Im poor {name}, i need money!!!")
    await asyncio.sleep(1.5)
    await sem.acquire()
    try:
        count = 0
        while count < 3:
            print(f'{name}: {str(datetime.datetime.now())} Yes!!! i get money')
            count = count + 1
        await asyncio.sleep(1.5)
    finally:
        sem.release()


async def main():
    # 锁
    sem_obj = asyncio.Semaphore()
    # 开始抢钱
    await asyncio.wait({poor(sem_obj, "XM"), poor(sem_obj, "XH")}, timeout=5)


if __name__ == "__main__":
    asyncio.run(main())

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


如果对本文有疑问或者发现不对的地方,希望能给予评论或者进群630300475,讨论一下。先写到这里了,到了游戏时间了,还有一部分,找时间补上

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值