首页 > 系统相关 >Python: 结合多进程和 Asyncio 以提高性能

Python: 结合多进程和 Asyncio 以提高性能

时间:2023-05-16 22:55:06浏览次数:41  
标签:num Python sum start 000 result 进程 asyncio Asyncio

动动发财的小手,点个赞吧!

简介

多亏了 GIL,使用多个线程来执行 CPU 密集型任务从来都不是一种选择。随着多核 CPU 的普及,Python 提供了一种多处理解决方案来执行 CPU 密集型任务。但是直到现在,直接使用多进程相关的API还是存在一些问题。

本文开始之前,我们还有一小段代码来帮助演示:

import time
from multiprocessing import Process


def sum_to_num(final_num: int) -> int:
    start = time.monotonic()

    result = 0
    for i in range(0, final_num+1, 1):
        result += i

    print(f"The method with {final_num} completed in {time.monotonic() - start:.2f} second(s).")
    return result

该方法接受一个参数并从 0 开始累加到该参数。打印方法执行时间并返回结果。

多进程存在的问题

def main():
    # We initialize the two processes with two parameters, from largest to smallest
    process_a = Process(target=sum_to_num, args=(200_000_000,))
    process_b = Process(target=sum_to_num, args=(50_000_000,))

    # And then let them start executing
    process_a.start()
    process_b.start()

    # Note that the join method is blocking and gets results sequentially
    start_a = time.monotonic()
    process_a.join()
    print(f"Process_a completed in {time.monotonic() - start_a:.2f} seconds")

    # Because when we wait process_a for join. The process_b has joined already.
    # so the time counter is 0 seconds.
    start_b = time.monotonic()
    process_b.join()
    print(f"Process_b completed in {time.monotonic() - start_b:.2f} seconds")

如代码所示,我们直接创建并启动多个进程,调用每个进程的start和join方法。但是,这里存在一些问题:

  1. join 方法不能返回任务执行的结果。
  2. join 方法阻塞主进程并按顺序执行它。

即使后面的任务比前面的任务执行得更快,如下图所示:

使用池的问题

如果我们使用multiprocessing.Pool,也会存在一些问题:

def main():
    with Pool() as pool:
        result_a = pool.apply(sum_to_num, args=(200_000_000,))
        result_b = pool.apply(sum_to_num, args=(50_000_000,))

        print(f"sum_to_num with 200_000_000 got a result of {result_a}.")
        print(f"sum_to_num with 50_000_000 got a result of {result_b}.")

如代码所示,Pool 的 apply 方法是同步的,这意味着您必须等待之前的 apply 任务完成才能开始执行下一个 apply 任务。

当然,我们可以使用 apply_async 方法异步创建任务。但是同样,您需要使用 get 方法来阻塞地获取结果。它让我们回到 join 方法的问题:

def main():
    with Pool() as pool:
        result_a = pool.apply_async(sum_to_num, args=(200_000_000,))
        result_b = pool.apply_async(sum_to_num, args=(50_000_000,))

        print(f"sum_to_num with 200_000_000 got a result of {result_a.get()}.")
        print(f"sum_to_num with 50_000_000 got a result of {result_b.get()}.")

直接使用ProcessPoolExecutor的问题

那么,如果我们使用 concurrent.futures.ProcesssPoolExecutor 来执行我们的 CPU 绑定任务呢?

def main():
    with ProcessPoolExecutor() as executor:
        numbers = [200_000_000, 50_000_000]
        for result in executor.map(sum_to_num, numbers):
            print(f"sum_to_num got a result which is {result}.")

如代码所示,一切看起来都很棒,并且就像 asyncio.as_completed 一样被调用。但是看看结果;它们仍按启动顺序获取。这与 asyncio.as_completed 完全不同,后者按照执行顺序获取结果:

使用 asyncio 的 run_in_executor 修复

幸运的是,我们可以使用 asyncio 来处理 IO-bound 任务,它的 run_in_executor 方法可以像 asyncio 一样调用多进程任务。不仅统一了并发和并行的API,还解决了我们上面遇到的各种问题:

async def main():
    loop = asyncio.get_running_loop()
    tasks = []

    with ProcessPoolExecutor() as executor:
        for number in [200_000_000, 50_000_000]:
            tasks.append(loop.run_in_executor(executor, sum_to_num, number))
        
        # Or we can just use the method asyncio.gather(*tasks)
        for done in asyncio.as_completed(tasks):
            result = await done
            print(f"sum_to_num got a result which is {result}")

由于上一篇的示例代码都是模拟我们应该调用的并发过程的方法,所以很多读者在学习之后在实际编码中还是需要帮助理解如何使用。所以在了解了为什么我们需要在asyncio中执行CPU-bound并行任务之后,今天我们将通过一个真实世界的例子来解释如何使用asyncio同时处理IO-bound和CPU-bound任务,并领略asyncio对我们的效率代码。

本文由mdnice多平台发布

标签:num,Python,sum,start,000,result,进程,asyncio,Asyncio
From: https://www.cnblogs.com/swindler/p/17407141.html

相关文章

  • Fast-GRPC: 用python轻松开发grpc接口
    简介Fast-GRPC旨在帮助开发者更加轻松快捷地使用Python开发gRPC接口。它的特点包括简化步骤、简单上手、快速开发,同时支持异步和同步代码,以及支持Middleware,灵感来自FastAPI。安装需要python3.7+pipinstallpython-fast-grpc快速上手下面是一个简单的Fast-GRPC示......
  • python字符串的45个内置方法
    1.字符串拼接和查找: 2.字符串分割替换和大小写操作: 3.字符串判断内容: 4.字符串剩下操作: ......
  • python学生管理系统笔记(+增删改查,但不存入数据库或文件中)
    原本的基础上+增删改查,但不存入数据库或文件中,就是数据只在一次运行的页面中进行增删改查,但是重新运行不会有之前的数据,因为没有更新到json或者数据库中。1.LoginPage.pyimporttkinterastkfromtkinterimportmessageboxfromdbimportdbfromMainPageimportMainPage......
  • Python 运算符
    1、算术运算符运算符描述实例+加-两个对象相加a+b输出结果30-减-得到负数或是一个数减去另一个数a-b输出结果-10*乘-两个数相乘或是返回一个被重复若干次的字符串a*b输出结果200/除-x除以yb/a输出结果2%取模-返回除法的余数......
  • python day 04
    字符串(str)作用:用来记录人的名字、家庭住址、性别等描述性质的状态定义字符串:方式1: name='kevin'#用的多方式2 name="kevin"#用的多方式3: name='''kevin'''(当三引号左边有赋值符号和变量名的时候,它是字符串,否则为注释)方式4: name=......
  • 掌握Python开发三部曲
    最近不少人在公众号后台或是微信直接问我Python开发需要掌握哪些或是问有木有书推荐或是问能不能给点电子书这年头最便宜的投资就是买、买、买书不管什么时候最好的提升就看书、看书、看书对于没有Python基础的,建议你买本《Python基础教程》成体系的把Python3所有基础均有涉及,你需......
  • Python数据离散化指南:手写if-elif语句与pandas中cut()方法的实现
    当我们进行数据分析时,有时候需要对数值型数据进行离散化,将其划分为不同的标签或类别。这样做可以方便我们进行统计和分析,并帮助我们更好地理解数据。在本文中,我们将介绍两种常见的离散化方法,并提供实现代码。方法一:使用条件语句第一种方法是使用条件语句来显式地检查输入值x......
  • How to use the Raspberry Pi and Python to control a DHT11 wet and temperate modu
    HowtousetheRaspberryPiandPythontocontrolaDHT11wetandtemperatemoduleAllInOne如何使用树莓派和Python来控制温湿度模块demos(......
  • python高级技术(死锁、递归锁、信号量、Event时间、进程池、线程池、协程)
    一死锁和递归锁(了解)进程也有死锁与递归锁,使用方法类似所谓死锁:是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。当你知......
  • Linux基础21 进程介绍, 进程监控状态ps, 进程相关命令pstree,pgrep,pidof, 动态进程监
    1.进程的管理:当我们运行一个程序,那么我们将该程序叫进程 进程线程协程 linux起服务会有给这个服务预分配的内存结构,windows没有 2.为什么要学进程管理?为了管理架构的服务 3.程序和进程的区别1)程序:开发写出来的代码,程序是永久存在的。 2)进程:它会随着程序的终止而销......