python协程详解

🔗其他内容推荐🔗:动手实现多头注意力机制

Python 中的协程是一种高效的并发机制,通过 asyncawait 关键字,可以轻松实现异步编程。协程适用于 I/O 密集型任务,能够显著提高程序的效率和响应速度。然而,它也有局限性,例如不能处理 CPU 密集型任务。在实际开发中,需要根据具体需求选择合适的并发方式。

协程是用户态的轻量级线程,它们的创建和切换开销非常小。协程的上下文切换不需要操作系统内核的介入,因此可以快速地在协程之间切换。这使得协程在处理大量并发任务时更加高效。

A协程执行到某处后保存上下文等待B协程执行,B协程执行到某处后保存上下文将执行权给A等待A的执行,A和B协程同处于一个线程中,协同完成任务,切换代价小。当线程执行I/O 操作(如网络请求、文件读写)时,会主动释放 GIL 并进入阻塞状态,然后操作系统介入切换线程,代价较大。所以协程更适合处理I/O 密集型任务

协程的优势:

  • 高效:没有线程切换的开销
  • 轻量:一个线程可运行成千上万个协程
  • 可控:调度由程序员控制,避免竞态条件。因为当前协程在什么地方等待其它协程执行,以及什么情况下继续执行,都是程序员显示指定的。
  • 节省资源:不需要多线程的锁机制

async 关键字:

  • 定义协程函数:使用 async def 定义一个协程函数。协程函数与普通函数不同,它不能直接执行,而是需要通过事件循环或 await 调用。

    async def my_coroutine():
        print("Hello")
        await asyncio.sleep(1)  # 模拟异步操作
        print("World")
    
    • 在这里,my_coroutine 是一个协程函数,而不是普通的函数。
    • 如果直接调用 my_coroutine(),它不会执行,而是返回一个协程对象。

await 关键字:

  • 暂停协程执行await 用于暂停当前协程的执行,直到等待的异步操作完成。

  • 等待异步操作await 后面必须是一个可等待对象(如协程对象、asyncio.Futureasyncio.Task 等)。

    await asyncio.sleep(1)
    """
    这是一个异步的睡眠函数,它不会阻塞当前线程,而是挂起当前的协程(coroutine)。在挂起期间,其他协程可以继续运行,从而充分利用 CPU 时间。
    而time.sleep()是一个同步的睡眠函数,它会阻塞当前线程 1 秒钟。如果在多线程程序中使用,意味着将切换到其它线程。
    """
    
    • 当执行到 await asyncio.sleep(1) 时,当前协程会暂停执行,让出控制权。
    • 一旦 asyncio.sleep(1) 完成,协程会从暂停的地方继续执行。

事件循环:

  • Python 的异步编程依赖于事件循环(Event Loop)。
  • 事件循环负责调度协程的执行,管理 I/O 操作,并在合适的时机恢复协程的执行。
  • 事件循环的生命周期通常由 asyncio.run() 管理。

协程的生命周期:

  1. 创建协程:使用 async def 定义协程函数,调用协程函数会返回一个协程对象。
  2. 暂停执行:在协程中使用 await 暂停执行,等待异步操作完成。
  3. 恢复执行:当异步操作完成时,事件循环会恢复协程的执行。

Demo1:

import asyncio
import aiofiles

# 异步写入文件
async def write_file(filename, content):
    async with aiofiles.open(filename, 'w') as file:  # 异步打开文件
        await file.write(content)  # 异步写入内容

# 异步读取文件
async def read_file(filename):
    async with aiofiles.open(filename, 'r') as file:  # 异步打开文件
        content = await file.read()  # 异步读取内容
        print(content)

# 主协程函数
async def main():
    filename = "example.txt"
    content = "Hello, World!"
    await write_file(filename, content)  # 异步写入文件
    await read_file(filename)  # 异步读取文件
    print("异步操作已完成")

# 运行主协程
asyncio.run(main())
  • aiofiles.open() 是一个异步操作,用于打开文件。
  • await file.write(content)await file.read() 分别用于异步写入和读取文件内容。

Demo2:

import asyncio

# 模拟从网站获取数据的协程
async def fetch_data(url, delay):
    print(f"Starting to fetch data from {url}")
    await asyncio.sleep(delay)  # 模拟异步 I/O 操作
    print(f"Finished fetching data from {url}")
    return f"Data from {url}"

# 模拟处理数据的协程
async def process_data(data):
    print(f"Processing {data}")
    await asyncio.sleep(1)  # 模拟数据处理的延迟
    print(f"Processed {data}")

# 主协程,负责并发执行多个任务
async def main():
    urls = [
        ("http://example.com", 2),
        ("http://example.org", 3),
        ("http://example.net", 1)
    ]

    # 创建一个任务列表
    tasks = []
    for url, delay in urls:
        task = asyncio.create_task(fetch_data(url, delay))  # 创建协程任务
        tasks.append(task)

    # 并发执行所有任务
    """
    asyncio.gather 会同时启动所有传入的任务,并将它们交给事件循环管理.
    事件循环是异步编程的核心机制。它负责:
    调度协程:事件循环会维护一个任务队列,队列中的任务是需要运行的协程。
    处理 I/O 事件:事件循环会监听 I/O 事件(如网络请求完成、文件读写完成等),并在事件发生时唤醒相应的协程。
    切换协程:当一个协程挂起时,事件循环会切换到其他就绪的协程运行。
    """
    results = await asyncio.gather(*tasks)  # 等待所有任务完成并获取结果
    
    # 处理每个任务的结果
    for data in results:
        await process_data(data)

# 运行主协程
asyncio.run(main())
if __name__ == '__main__':
    pass

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值