一文掌握python协程asyncio(二)

本文深入探讨了Python的asyncio库在多线程和多进程中的高级应用,包括如何在多线程和多进程中结合使用协程,以及asyncio.to_thread和run_in_executor的使用。文章通过实例详细解释了如何在多线程环境中创建事件循环以驱动并发I/O操作,并展示了如何结合进程池和协程实现更高效的任务管理。此外,还对比了run_in_executor和run_coroutine_threadsafe的区别,帮助开发者更好地理解和选择合适的并发模型。最后,文章讨论了协程、多线程、多进程在I/O密集型任务中的选择和优缺点,以及如何判断性能瓶颈。

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

目录

六、进阶功能

5、多线程与协程结合

6、多进程与协程结合

1)如何在多进程中使用协程(启用一个子进程),并确保能够根据信号正常结束。

2)如何在多个子进程中分别运行不同的协程任务,并确保它们能够根据信号正常结束。

3)更好的方案,结合进程池和协程来实现更高效的任务管理

7、asyncio.to_thread

8、跨线程调度:asyncio.run_coroutine_threadsafe

9、run_in_executor

1)使用默认的线程池:如果不指定执行器(executor),那么 asyncio 会使用一个内部的线程池来执行传递给它的函数。

2)自定义执行器:也可以提供一个自定义的 concurrent.futures.Executor 实例,比如 ThreadPoolExecutor 或 ProcessPoolExecutor,来控制线程或进程的创建和管理。

10、run_in_executor和run_coroutine_threadsafe有啥区别

1)run_in_executor

2)run_coroutine_threadsafe

七、协程、多进程、多线程的区别及使用

1、使用选择

2、举个例子:I/O密集型任务(如网络请求、文件读写),如下载一百万张图片,多线程、多进程、协程如何选择,各自的优势、缺点、瓶颈

1)协程

2)多线程

3)多进程

4)综合考虑

3、如何确定服务器协程、多线程、进程是否到了瓶颈

1)CPU 使用率

2)内存使用率

3)I/O等待时间

4)网络带宽和磁盘I/O

5)上下文切换

6)系统监控工具

7)应用程序日志和性能测试

8)线程/进程数量与并发度


接上篇,一文掌握python协程asyncio(一)-优快云博客

六、进阶功能

5、多线程与协程结合

结合多线程和协程的一个常见用途是创建一个既能够处理高并发I/O操作,又能在需要时利用多核CPU进行计算的程序。下面是一个简单的示例,展示了如何在一个多线程环境中使用asyncio来驱动并发的I/O操作,同时在每个线程内部使用单独的事件循环来管理协程。

import asyncio
import threading


async def my_coroutine(id, thread_name):
    print(f"Starting coroutine {id} in thread {thread_name}...")
    await asyncio.sleep(1)  # 模拟异步I/O操作,等待1秒
    print(f"Ending coroutine {id} in thread {thread_name}...")



def run_coroutines(thread_name):
    # 初始化并设置事件循环
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)

    # 创建协程任务并运行
    tasks = [loop.create_task(my_coroutine(i, thread_name)) for i in range(3)]
    loop.run_until_complete(asyncio.wait(tasks))

    # 关闭事件循环
    loop.close()
    print(f"Event loop in thread {thread_name} closed.")



def main():
    threads = []
    for i in range(2):  # 创建并启动两个线程
        thread_name = f"Thread-{i + 1}"
        t = threading.Thread(target=run_coroutines, args=(thread_name,))
        t.start()
        threads.append(t)
        print(f"Started thread {thread_name}.")

    # 等待所有线程执行完毕
    for t in threads:
        t.join()
        print(f"Joined thread {t.name}.")


if __name__ == "__main__":
    main()

在上面的示例中,创建了两个线程,每个线程都运行一个事件循环并执行三个协程。由于协程是协作式的,它们不会真正并行执行(在同一个线程内),但在多线程环境中,不同的线程可以并行执行不同的协程。这样,当某些协程等待I/O操作完成时,其他线程中的协程可以继续执行,从而提高了整体效率。

然而,需要注意的是,过度使用多线程和协程可能会增加程序的复杂性和调试难度。在设计并发程序时,应该根据具体的应用场景和需求来选择最适合的并发模型。

6、多进程与协程结合

1)如何在多进程中使用协程(启用一个子进程),并确保能够根据信号正常结束。

在Python中,实现跨进程的异步通信和协作,同时在多进程中使用协程,需要利用进程间的通信机制(如管道、队列、共享内存等)与事件循环的隔离性相结合。使用multiprocessing.Event来同步停止信号,并在子进程中直接使用loop.run_until_complete()来运行协程直到收到停止信号,避免了事件循环的无限循环问题。

import asyncio
import multiprocessing
import time


async def my_coroutine(stop_event):
    # is_set(): 返回内部标志的状态,如果标志被设置(True),则返回True,否则返回False。
    while not stop_event.is_set():
        print("Coroutine running")
        await asyncio.sleep(1)
    print("Coroutine stopped")


def start_loop(stop_event):
    loop = asyncio.new_event_loop()  # 创建新的事件循环
    asyncio.set_event_loop(loop)  # 设置当前线程的事件循环
    try:
        # 在事件循环中运行协程直到完成
        loop.run_until_complete(my_coroutine(stop_event))
    finally:
        loop.stop()  # 停止事件循环
        loop.close()  # 关闭事件循环


if __name__ == "__main__":
    # 创建一个用于控制协程停止的事件
    stop_event = multiprocessing.Event()

    # 开始一个新的进程来运行协程
    process = multiprocessing.Process(target=start_loop, args=(stop_event,))
    process.start()

    time.sleep(5)  # 让协程运行一段时间
    print("Set stop event.")
    # 设置停止事件,通知协程结束
    stop_event.set()  # 设置内部标志为True

    # 等待子进程结束
    process.join()
    print("All done.")

这个例子中,使用了multiprocessing.Event来传递停止信号,这是一个支持跨进程通信的同步原语。start_loop函数中,我们通过loop.run_until_complete(my_coroutine(stop_event))来等待协程直到它自然结束(即stop_event.is_set()变为True),这样应该能避免事件循环无限运行的问题,并在设置停止信号后正常结束进程。

2)如何在多个子进程中分别运行不同的协程任务,并确保它们能够根据信号正常结束。

每个进程需要有自己的事件循环来管理协程,以下是一个示例,展示了如何在多个子进程中分别运行不同的协程任务,并确保它们能够根据信号正常结束:

import asyncio
import multiprocessing
import time


async def my_coroutine(name, stop_event):
    counter = 0
    while not stop_event.is_set():
        print(f"{name} running, counter: {counter}")
        await asyncio.sleep(1)
        counter += 1
    print(f"{name} stopping...")


def start_loop(name, stop_event):
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    try:
        # 直接运行协程,并监视停止事件
        loop.run_until_complete(my_coroutine(name, stop_event))
    finally:
        loop.stop()
        loop.close()


if __name__ == "__main__":
    # 创建两个事件,分别用于控制两个子进程的停止
    stop_event1 = multiprocessing.Event()
    stop_event2 = multiprocessing.Event()

    # 启动两个子进程,每个进程运行一个不同的协程
    process1 = multiprocessing.Process(target=start_loop, args=("Coroutine 1", stop_event1,))
    process2 = multiprocessing.Process(target=start_loop, args=("Coroutine 2", stop_event2,))

    process1.start()
    process2.start()

    time.sleep(10)  # 让协程运行一段时间
    print("Setting stop events...")
    stop_event1.set()  # 设置第一个子进程的停止事件
    time.sleep(2)  # 等待一段时间,以便观察第一个协程停止
    stop_event2.set()  # 设置第二个子进程的停止事件

    # 确保所有进程结束
    process1.join()
    process2.join()
    print("All processes done.")

这个示例中,定义了两个协程my_coroutine,分别在两个子进程中运行,并通过各自的stop_event来控制何时结束。主进程等待一段时间后,分别设置这两个事件,通知对应的子进程中的协程停止运行。每个子进程拥有独立的事件循环,保证了它们可以并发执行且互不影响。

3)更好的方案,结合进程池和协程来实现更高效的任务管理

考虑到concurrent.futures.ProcessPoolExecutor本身不直接支持协程,采用一种折衷的方法:在每个子进程中手动创建事件循环,并通过进程池来管理这些子进程。这里的关键是通过进程间通信来传递停止信号,而不是直接在协程中使用事

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值