python -- 异步I/O

本文介绍了Python异步I/O的概念,重点讲解了如何使用async/await语法进行异步操作,并通过aiohttp库演示了异步HTTP请求的实现。异步操作允许在一个主线程中并发执行多个协程,提高程序效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

异步操作目的是使用一个主线程实现多个协程并发执行

定义一个函数,该函数是一个消费者的生成器,使用装饰器@asyncio.coroutine 把生成器变成协程,协程的程序是可以中断再执行的,异步操作需要在协程中中通过yield from完成;yield from语法可以让我们方便地调用另一个generator,生成器既可以是生产者也可以是消费者,当一个任务的程序执行到这里时,会中断程序,调用其他的生成器,yield返回值是需要时间的,但是主线程是不会等待这个任务结束的,而是会去event_loop中执行其他可执行的任务,等到yield from拿到值时,这时会继续执行中断的程序

异步操作是通过yield from 来实现的 当程序执行到该语句时,会由于避免等待而去执行其他任务,yield from 后面可以跟携程或者生成器
当哪个携程执行不了了, 线程会先执行其他的协程

import asyncio
import threading

# 把生成器变成协程
@asyncio.coroutine
def countdown(name, num):
    # 打印当前的线程
    print(threading.current_thread())
    while num > 0:
        # 打印任务的名称 和 名字 这一句是为了验证任务的并
        发执行,任务1 执行到yield from会中断程序,线程会
        执行任务2 任务2 依然打印出名字 之后 执行到 yield
        from 中断程序, 执行任务3 ,这时就会把任务123
        的名字同时打印出来,等到任务yield 拿到值时中断的程序
        会接着往下执行
        print((f'Countdown[{name}]: {num}'))
        # 异步执行 - 非阻塞式 asyncio.sleep()也是一个
        协程 异步执行靠的就是yield from 执行其他的协程,
        执行其他协程时需要耗费时间,此时程序会终止到这里,
        但是异步执行时,是不会阻塞在这里的, 主线程并未等待,
        因此可以实现并发执行任务
        这里yield得到的值是None,
        如果换成是真正的I/O操作这里就可以得到返回值了
        yield from asyncio.sleep(1)
        # 同步执行 - 阻塞式 如果把休息时间写成这样的话, 程序执
        行到这里会卡在这里,形成阻塞,也不会去执行其他可执行的任
        务了
        # time.sleep(1)
        num -=  1


def main():
    # 通过下面的event_loop实现多任务的并发执行
    loop = asyncio.get_event_loop()
    # 异步I/O - 虽然只有一个线程但是两个任务相互之间不阻塞
    tasks = [
        countdown('A', 10), countdown('B', 5)
    ]
    loop.run_until_complete(asyncio.wait(tasks))
    # 任务执行完了,关闭循环
    loop.close()


if __name__ == '__main__':
    main()

asyncio.sleep(1), 可以看做是一个耗时一秒的I/O操作,在此期间,主线程并未等待,而是去执行EventLoop中其他可以执行的coroutine了,因此可以实现并发执行。
如果把asyncio.sleep()换成真正的IO操作,则多个coroutine就可以由一个线程并发执行,具体操作看廖雪峰官网


async/await

用asyncio提供的@asyncio.coroutine可以把一个generator标记为coroutine类型,然后在coroutine内部用yield from调用另一个coroutine实现异步操作。

为了简化并更好地标识异步IO,从Python 3.5开始引入了新的语法async和await,可以让coroutine的代码更简洁易读
请注意,async和await是针对coroutine的新语法,要使用新的语法,只需要做两步简单的替换:

把@asyncio.coroutine替换为async;
把yield from替换为await。
@asyncio.coroutine
def hello():
    print("Hello world!")
    r = yield from asyncio.sleep(1)
    print("Hello again!")
新用法:
async def hello():
    print("Hello world!")
    r = await asyncio.sleep(1)
    print("Hello again!")

实例示范:

import asyncio
import requests
import time

async def download(url):
    print('Fetch:', url)
    # 联网下载可能耗时, 异步取, 加上yield变成了协程
    # time.sleep(10) 阻塞式的
    # 程序执行到这里 中断3秒 上面的print并发执行
    await asyncio.sleep(3)
    resp = requests.get(url)
    print(resp.status_code)
    # 程序执行到这里中断5秒 上面的print并发执行
    await asyncio.sleep(5)
    print(url)
    # print(resp.headers)


def main():
    loop = asyncio.get_event_loop()
    urls = [
        'https://www.baidu.com',
        'http://www.sohu.com',
        'http://www.sina.com',
        'https://www.taobao.com',
        'http://www.qq.com'
    ]
    # 启动下载任务
    tasks = [download(url) for url in urls]
    # 异步执行任务
    loop.run_until_complete(asyncio.wait(tasks))


if __name__ == '__main__':
    main()

程序运行结果如下图:
这里写图片描述


aiohttp

实例示范:

import asyncio
import aiohttp


async def download(url):
    print('Fetch:', url)
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as resp:
            print(url, '--->', resp.status)
            print(url, '--->', resp.cookies)
            # \n\n意思是隔两个空行打印一次
            # await 打印可能耗时, 中断异步执行, 
            # print('\n\n', await resp.text())


def main():
    loop = asyncio.get_event_loop()
    urls = [
        'https://www.baidu.com',
        'http://www.sohu.com',
        'http://www.sina.com',
        'https://www.taobao.com',
        'http://www.qq.com'
    ]
    tasks = [download(url) for url in urls]
    loop.run_until_complete(asyncio.wait(tasks))
    loop.close()


if __name__ == '__main__':
    main()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值