asyncio note

asyncio note

一、什么是异步编程?

异步编程是一种编程方式,它允许程序在等待某些任务(如网络请求、文件操作)完成时继续执行其他任务,而不是停下来等。这样可以提高程序的效率,特别是在处理 I/O 密集型任务时。

在传统的同步编程中,任务是按顺序执行的。假设有两个任务,任务1需要2秒,任务2需要1秒,那么总共需要3秒才能完成。

import time

def task_1():
    print("开始任务 1")
    time.sleep(2)  # 模拟一个需要2秒的操作
    print("任务 1 完成")

def task_2():
    print("开始任务 2")
    time.sleep(1)  # 模拟一个需要1秒的操作
    print("任务 2 完成")

task_1()
task_2()

在上面的代码中,task_1task_2 是按顺序执行的,一个任务结束了才开始下一个。

二、异步编程的基本概念

  1. 协程(Coroutine):协程是 Python 中一种特殊的函数,它可以在执行过程中暂停,并在稍后恢复执行。协程的定义使用 async def 关键字。

  2. await 关键字await 用于暂停协程的执行,等待另一个协程完成。只有在协程中才能使用 await

  3. 事件循环(Event Loop):事件循环是一个循环,它不断地运行,调度任务的执行。在异步编程中,事件循环是用来管理和运行多个任务的核心。

三、asyncio 入门示例

1. 定义一个协程

首先,定义一个简单的协程函数,使用 async def 关键字。

import asyncio

async def say_hello():
    print("Hello")
    await asyncio.sleep(1)  # 模拟一个异步操作,比如等待1秒
    print("World")

# 在这个例子中,say_hello 是一个协程函数。在执行到 await asyncio.sleep(1) 时,程序会暂停1秒钟。
2. 运行协程

定义了协程之后,需要一个事件循环来运行它。

# 创建一个事件循环并运行协程
loop = asyncio.get_event_loop()
loop.run_until_complete(say_hello())
loop.close()

这里,创建了一个事件循环 loop,并使用 run_until_complete 方法来运行 say_hello() 协程,直到它完成。

3. 运行方法

可以使用 asyncio.run() 来运行协程(Python 3.7 及以上版本)。

import asyncio

async def say_hello():
    print("Hello")
    await asyncio.sleep(1)
    print("World")

# 使用 asyncio.run() 运行协程
asyncio.run(say_hello())

四、并发执行多个协程

一个协程在等待时,事件循环可以去运行其他协程,这样就可以并发地运行多个任务。

import asyncio

async def task_1():
    print("Task 1 start")
    await asyncio.sleep(2)
    print("Task 1 end")

async def task_2():
    print("Task 2 start")
    await asyncio.sleep(1)
    print("Task 2 end")

async def main():
    await asyncio.gather(task_1(), task_2())

asyncio.run(main())

在这个例子中,task_1task_2 会并发执行。即使 task_1 需要 2 秒才能完成,task_2 只需要 1 秒,程序的总执行时间是 2 秒,而不是 3 秒,因为两个任务是同时进行的。

五、小结

可以用以下步骤进行练习。

  1. 创建自己的协程:尝试创建一个简单的协程函数,比如 async def count_to_five(),它每秒打印一个数字,从 1 数到 5。

  2. 使用 await 等待:在你的协程中使用 await asyncio.sleep(1) 让它每秒打印一个数字。

  3. 使用 asyncio.run():运行你的协程,看看它是如何工作的。

  4. 并发执行多个协程:尝试创建多个协程,并用 asyncio.gather() 并发运行它们。

六、有效地应用 asyncio

1. 更多协程和任务管理

更好地管理多个协程和任务,特别是在需要的时候取消任务,或者在所有任务完成后继续执行。

示例:取消任务

import asyncio

async def long_running_task():
    print("Task started")
    try:
        await asyncio.sleep(10)
    except asyncio.CancelledError:
        print("Task was cancelled")
    finally:
        print("Task ending")

async def main():
    task = asyncio.create_task(long_running_task())

    await asyncio.sleep(1)
    task.cancel()  # 取消任务
    try:
        await task  # 等待任务结束,捕获异常
    except asyncio.CancelledError:
        print("Caught task cancellation")

asyncio.run(main())
2. 使用 asyncio 的同步工具

asyncio 提供了一些类似于线程同步的工具,如 LockEventConditionSemaphore,用于控制异步任务之间的并发。

示例:使用锁

import asyncio

lock = asyncio.Lock()

async def task_1():
    async with lock:
        print("Task 1 is running")
        await asyncio.sleep(2)
        print("Task 1 is done")

async def task_2():
    async with lock:
        print("Task 2 is running")
        await asyncio.sleep(1)
        print("Task 2 is done")

async def main():
    await asyncio.gather(task_1(), task_2())

asyncio.run(main())

在这个示例中,task_1task_2 是互斥的,因为它们都使用了同一个锁 lock。通过这种方式,可以避免多个协程同时访问共享资源。

3. 队列和生产者-消费者模式

使用 asyncio.Queue 实现生产者-消费者模式,可以在多个协程之间传递数据。

示例:生产者-消费者

import asyncio

async def producer(queue):
    for i in range(5):
        await asyncio.sleep(1)
        item = f'item {i}'
        await queue.put(item)
        print(f'Produced {item}')

async def consumer(queue):
    while True:
        item = await queue.get()
        if item is None:  # 检查结束标志
            break
        print(f'Consumed {item}')
        await asyncio.sleep(2)  # 模拟处理时间

async def main():
    queue = asyncio.Queue()
    producer_task = asyncio.create_task(producer(queue))
    consumer_task = asyncio.create_task(consumer(queue))

    await producer_task
    await queue.put(None)  # 向队列发送结束标志
    await consumer_task

asyncio.run(main())

这个例子展示了如何使用 asyncio.Queue 在生产者和消费者之间传递数据。

4. 异步文件操作

虽然 Python 的内置文件操作是阻塞的,但可以使用第三方库(如 aiofiles)进行异步文件操作。在不阻塞事件循环的情况下读写文件。

示例:异步文件读写

首先,安装 aiofiles

pip install aiofiles

然后,编写异步文件操作代码:

import asyncio
import aiofiles

async def write_to_file():
    async with aiofiles.open('example.txt', mode='w') as file:
        await file.write('Hello, asyncio!\n')

async def read_from_file():
    async with aiofiles.open('example.txt', mode='r') as file:
        content = await file.read()
        print(content)

async def main():
    await write_to_file()
    await read_from_file()

asyncio.run(main())
5. HTTP 请求和异步网络编程

示例:使用 aiohttp 进行异步 HTTP 请求

首先,安装 aiohttp

pip install aiohttp

然后,编写异步 HTTP 请求代码:

import aiohttp
import asyncio

async def fetch(session, url):
    async with session.get(url) as response:
        return await response.text()

async def main():
    async with aiohttp.ClientSession() as session:
        html = await fetch(session, 'https://www.example.com')
        print(html)

asyncio.run(main())
6. 深入理解事件循环和任务调度

使用 asyncio.get_event_loop() 创建和管理事件循环;在不同的线程中运行事件循环,以及使用 asyncio.run_coroutine_threadsafe() 在其他线程中安全地运行协程。

7. 错误处理和调试

使用调试工具(如 asyncio.get_running_loop().set_debug(True))来发现和解决问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值