• 浅谈Python异步编程


    1. 异步编程概述

    异步编程是一种编程范式,用于处理那些需要等待I/O操作完成或者耗时任务的情况。在传统的同步编程中,代码会按照顺序逐行执行,直到遇到一个耗时操作,它会阻塞程序的执行直到操作完成。这种阻塞式的模型在某些场景下效率低下,因为代码在等待操作完成时无法执行其他任务。

    异步编程通过使用非阻塞I/O和协程(coroutine)来提高效率。协程是一种特殊的函数,可以在执行过程中暂停和恢复。当一个协程遇到一个耗时操作时,它会暂停自己的执行,让出控制权给其他协程,从而实现并发执行。async/await关键字是Python中处理协程的语法工具

    2. async/await关键字

    async关键字

    async是一个关键字,用于定义一个协程函数。协程函数可以通过使用await关键字来暂停自身的执行,等待其他协程或异步操作完成。

    以下是一个简单的示例,展示了如何定义一个协程函数:

    1
    2
    3
    4
    5
    6
    7
    import asyncio
     
    async def my_coroutine():
        print("Coroutine started")
        await asyncio.sleep(1)
        print("Coroutine resumed")
        return "Result"

    my_coroutine是一个协程函数。它使用了async关键字进行定义,并包含了一个await语句来暂停执行。

    await关键字

    await是另一个关键字,用于暂停协程函数的执行,等待另一个协程、异步操作或者Future对象完成。

    以下是一个使用await的示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import asyncio
     
    async def my_coroutine():
        print("Coroutine started")
        await asyncio.sleep(1)
        print("Coroutine resumed")
        return "Result"
     
    async def main():
        result = await my_coroutine()
        print(f"Result: {result}")
     
    asyncio.run(main())

    在上面的示例中,main函数是一个协程函数,它使用await关键字来等待my_coroutine协程函数的执行结果。当await语句执行时,main函数会暂停自身的执行,直到my_coroutine协程函数完成并返回结果。

    需要注意的是,await关键字只能在协程函数中使用。如果你在普通的同步函数中使用await,会导致语法错误。

    3. 异步事件循环

    异步编程的核心是事件循环(event loop)。事件循环负责调度和执行协程,确保它们按照正确的顺序执行。

    在Python中,可以使用asyncio模块提供的事件循环来创建和管理协程。

    以下是一个使用事件循环的示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import asyncio
     
    async def my_coroutine():
        print("Coroutine started")
        await asyncio.sleep(1)
        print("Coroutine resumed")
        return "Result"
     
    async def main():
        result = await my_coroutine()
        print(f"Result: {result}")
     
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

    在上面的示例中,asyncio.get_event_loop()用于获取默认的事件循环对象。然后,通过调用run_until_complete方法来运行main协程函数,直到它完成

    异步编程最常见的用例是处理I/O操作,例如读写文件或与网络通信。在传统的同步编程中,这些操作会阻塞程序的执行,直到操作完成。而在异步编程中,可以使用异步IO操作来实现非阻塞的并发执行。

    4. 异步IO操作

    Python提供了asyncio模块来处理异步IO操作。asyncio中的一些常用函数和类包括:

    • asyncio.sleep(delay): 创建一个休眠指定时间的协程。
    • asyncio.open_connection(host, port): 创建一个协程,用于与指定的主机和端口建立网络连接。
    • asyncio.open_unix_connection(path): 创建一个协程,用于与指定路径的UNIX域套接字建立连接。
    • asyncio.start_server(client_connected_cb, host, port): 创建一个协程,用于监听指定主机和端口的连接请求,并在每次连接时调用client_connected_cb回调函数。

    以下是一个使用异步IO操作的示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    import asyncio
     
    async def read_data():
        # 模拟异步IO读取操作
        await asyncio.sleep(1)
        return "Data"
     
    async def write_data(data):
        # 模拟异步IO写入操作
        await asyncio.sleep(1)
        print(f"Data written: {data}")
     
    async def main():
        data = await read_data()
        await write_data(data)
     
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

    在上面的示例中,read_datawrite_data函数模拟了异步的IO读取和写入操作。在main函数中,我们使用await关键字等待读取操作完成,然后将结果传递给写入操作。

    执行步骤如下:

    1. 首先,创建一个事件循环(Event Loop)对象,使用asyncio.get_event_loop()获取默认的事件循环。

    2. 定义了三个协程函数:read_data()write_data()main()

    3. 调用loop.run_until_complete(main()),将main()协程任务提交给事件循环并运行,直到main()协程完成。

    4. main()协程中,首先调用read_data()协程函数。这会启动read_data()协程,并在await asyncio.sleep(1)处暂停执行,等待1秒钟。

    5. 在暂停执行的同时,事件循环可以切换到其他可运行的协程,例如write_data()协程。

    6. write_data()协程同样会启动,并在await asyncio.sleep(1)处暂停执行,等待1秒钟。

    7. write_data()协程暂停执行时,事件循环没有其他可运行的协程,因此它会等待,直到有其他协程可运行。

    8. 在等待1秒钟后,read_data()协程恢复执行。它完成后,返回结果"Data"。

    9. main()协程接收到read_data()协程的返回结果,将其赋值给data变量。

    10. main()协程继续执行,调用write_data(data)协程。

    11. write_data()协程恢复执行,打印出"data"的值。

    12. main()协程完成,事件循环结束。

    在这个过程中,通过使用await关键字,协程能够在等待IO操作完成时暂停执行,并允许事件循环切换到其他协程。这种方式下,IO操作可以以异步的方式执行,而不会阻塞整个程序的执行流程。

    5. 并发执行多个协程

    异步编程的一个关键优势是能够并发执行多个协程,以提高程序的性能。

    asyncio提供了多种方式来实现协程的并发执行,其中最常用的方式是使用asyncio.gather函数。

    以下是一个并发执行多个协程的示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import asyncio
     
    async def coroutine1():
        await asyncio.sleep(1)
        print("Coroutine 1 completed")
     
    async def coroutine2():
        await asyncio.sleep(2)
        print("Coroutine 2 completed")
     
    async def coroutine3():
        await asyncio.sleep(0.5)
        print("Coroutine 3 completed")
     
    async def main():
        await asyncio.gather(coroutine1(), coroutine2(), coroutine3())
     
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

    在上面的示例中,coroutine1coroutine2coroutine3是三个协程函数。在main函数中,我们使用asyncio.gather函数来并发执行这三个协程。asyncio.gather接受一个可变数量的协程参数,并返回一个新的协程,该协程在所有给定的协程完成后完成。执行循序为当执行到 coroutine1中的await时,此协程会挂起,执行权交给新的协程 coroutine2开始执行,以此类推。当 coroutine3 等待0.5s执行完毕后,执行权重新回到coroutine3 ,继续执行一下语句,其他同理。

    需要注意的是,Python的协程是单线程的,通过事件循环来实现并发执行。当一个协程遇到阻塞的IO操作时,它会暂停自身的执行,并切换到下一个可执行的协程。这种切换是由事件循环调度

  • 相关阅读:
    【TES720D-KIT】青翼自研基于复旦微FMQL20S400全国产化ARM开发套件(核心板+底板)
    linux并发服务器 —— linux网络编程(七)
    如何“使用Docker快速安装Jenkins,在CentOS7”?
    HC32F4A0 以太网调试问题(测试发现各种逻辑问题)
    流媒体播放器EasyPlayer.js无法播放H.265的情况是什么原因?该如何解决?
    C++ 变量的声明和初始化方式
    WireShark 常用协议分析
    无心剑中文随感《探求真谛》
    Django基础入门操作 (Django-01)
    spring cloud-注册中心(Eureka)
  • 原文地址:https://www.cnblogs.com/beyond-tester/p/17786087.html