python中使用多进程执行计算密集型任务(完整代码示例)

问题

你有大量的数据,需要对这些大量的数据进行相关计算,并且计算的结果要和给定的数据顺序对应一致。

解决方案

创建两个被多个子进程共享的 Queue 对象,一个用于主进程将数据分块放入该 Queue 对象中,然后子进程从该 Queue 对象中取出数据进行计算;一个用于子进程计算的结果放入 Queue 对象中,然后主进程从该 Queue 对象中取出计算结果。

假设密集型计算任务(函数)如下:

def cal(nums: List[int]):
    ans = []
    for num in nums:
        res = 0
        for i in range(num):
            res += i + i ** i + i * i
        ans.append(res)
    return ans

假设(模拟)需要有如下大量的数据需要进行计算

nums = [10] * 10000000     # 7个0

创建用于进程间通信的 Queue 对象,然后将 Queue 对象作为子进程执行函数的参数,子进程从 Queue 对象中获取需要计算的数据;

import multiprocessing as mp

def start_multi_precess_pool(pool_nums: int):
    """
    pool_nums: 创建的进程数量
    """
    # 使用 spawn 启动进程,具体请参考:https://docs.python.org/3/library/multiprocessing.html?highlight=multiprocessing#contexts-and-start-methods
    ctx = mp.get_context("spawn")

    # 创建用于输入和输出的 Queue 对象
    input_queue = ctx.Queue()
    output_queue = ctx.Queue()  
    process = []
    
    # 创建 pool_nums 数量的进程
    for i in range(pool_nums):
        # 创建子进程,将 Queue 对象作为进程执行函数的参数,用于进程间的通信;
        # 并且给进程传递序号 i,用以保证后续计算结果的有序返回;
        # cal_multi_process_worker 函数用从 Queue 获取数据,并执行密集计算任务;
        p = ctx.Process(target=cal_multi_process_worker, args=(i, input_queue, output_queue))
        p.start()
        process.append(p)

    # 将 Queue 对象和进程对象集合返回,将 主进程将数据传递给 Queue 对象的行为 封装为另一个函数
    return {"input": input_queue, "output": output_queue, "process": process}

子进程从 Queue 对象中拿去数据并执行计算;

def cal_multi_process_worker(id: int, input_queue: Queue, output_queue: Queue):
    while True:
        try:
            id, nums = input_queue.get()
            ans = cal(nums)
            output_queue.put([id, ans])
        except queue.Empty:
            break

将数据分块,并写入 Queue 对象;然后从 Queue 对象取出计算后的结果;

def cal_multi_process(nums: List[int], pool: Dict[str, object], chunk_size: int = None):
    """
    对数据进行分块,将分块后的数据传入Queue对象
    """
    if chunk_size is None:
        chunk_size = min(math.ceil(len(nums) / len(pool["process"]) / 10), 500)

    input_queue = pool["input"]
    last_chunk_id = 0
    chunk = []

    for num in nums:
        chunk.append(num)
        if len(chunk) >= chunk_size:
            input_queue.put([last_chunk_id, chunk])
            last_chunk_id += 1
            chunk = []

    if len(chunk) > 0:
        input_queue.put([last_chunk_id, chunk])
        last_chunk_id += 1

    # 从 Queue 对象中的数据获取计算后的结果
    output_queue = pool["output"]
    
    # 根据数据分块号对返回的结果进行排序,以保证得到的计算结果和输入的数据顺序保持一致;
    result_list = sorted([output_queue.get() for _ in range(last_chunk_id)], key=lambda x : x[0])
    
    return result_list

关闭子进程,释放相关资源;

def stop_multi_process_pool(pool):
    # 终止子进程
    for p in pool["process"]:
        p.terminate()

    # 释放子进程的相关资源
    for p in pool["process"]:
        p.join()
        p.close()

    pool["input"].close()
    pool["output"].close()

完整代码

import multiprocessing as mp
from multiprocessing import Queue
import queue
import time
from typing import List, Dict
import math

def cal(nums: List[int]):
    """
    计算密集型函数
    """
    ans = []
    for num in nums:
        res = 0
        for i in range(num):
            res += i + i ** i + i * i
        ans.append(res)
    return ans

def cal_multi_process_worker(input_queue: Queue, output_queue: Queue):
    """
    子进程从Queue对象中获取数据,并执行计算
    """
    while True:
        try:
            id, nums = input_queue.get()
            ans = cal(nums)
            output_queue.put([id, ans])
        except queue.Empty:
            break
            
            
def cal_multi_process(nums: List[int], pool: Dict[str, object], chunk_size: int = None):
    """
    对数据进行分块,将分块后的数据传入Queue对象
    """
    if chunk_size is None:
        chunk_size = min(math.ceil(len(nums) / len(pool["process"]) / 10), 500)

    input_queue = pool["input"]
    last_chunk_id = 0
    chunk = []

    for num in nums:
        chunk.append(num)
        if len(chunk) >= chunk_size:
            input_queue.put([last_chunk_id, chunk])
            last_chunk_id += 1
            chunk = []

    if len(chunk) > 0:
        input_queue.put([last_chunk_id, chunk])
        last_chunk_id += 1

    # 从 Queue 对象中的数据获取计算后的结果
    output_queue = pool["output"]
    
    # 根据数据分块号对返回的结果进行排序,以保证得到的计算结果和输入的数据顺序保持一致;
    result_list = sorted([output_queue.get() for _ in range(last_chunk_id)], key=lambda x : x[0])
    
    return result_list

def start_multi_precess_pool(pool_nums: int):
    """
    pool_nums: 创建的进程数量
    """
    # 使用 spawn 启动进程,具体请参考:https://docs.python.org/3/library/multiprocessing.html?highlight=multiprocessing#contexts-and-start-methods
    ctx = mp.get_context("spawn")

    # 创建用于输入和输出的 Queue 对象
    input_queue = ctx.Queue()
    output_queue = ctx.Queue()  
    process = []
    
    # 创建 pool_nums 数量的进程
    for i in range(pool_nums):
        # 创建子进程,将 Queue 对象作为进程执行函数的参数,用于进程间的通信;
        # 并且给进程传递序号 i,用以保证后续计算结果的有序返回;
        # cal_multi_process_worker 函数用从 Queue 获取数据,并执行密集计算任务;
        p = ctx.Process(target=cal_multi_process_worker, args=(input_queue, output_queue))
        p.start()
        process.append(p)

    # 将 Queue 对象和进程对象集合返回,将主进程将数据传递给 Queue 对象的行为 封装为另一个函数
    return {"input": input_queue, "output": output_queue, "process": process}

def stop_multi_process_pool(pool):
    # 终止子进程
    for p in pool["process"]:
        p.terminate()

    # 释放子进程的相关资源
    for p in pool["process"]:
        p.join()
        p.close()

    pool["input"].close()
    pool["output"].close()

if __name__ == "__main__":
    start_time = time.time()

    nums = [10] * 10000000   # 7个0

    # pool = start_multi_precess_pool(6)
    # ans = cal_multi_process(nums, pool)
    # stop_multi_process_pool(pool)
		# 25.43005394935608

    ans = cal(nums)    # 6.784837245941162
    total_time = time.time() - start_time
    print(f"total_time = {total_time}")
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Python ,可以使用多线程或多进程来提高从数据库读取大量数据的速。选择使用多线程还是多程取决于具体的应用场和需求。 使用多线程读取时,可以使用 `threading` 模块创建和管理线程。每个线程责读取数据库的一部分,并将其存储在适当的数据构。这样可以同时读取多个块,从而提高读取速度。需要注意的是,如果数据库连接是共享的,需要考虑线程安全性。 下面是一个简单的示例代码,使用多线程从数据库读取数据: ```python import threading import pymysql # 设置数据库连接参数 db_config = { 'host': 'localhost', 'user': 'username', 'password': 'password', 'database': 'database_name' } # 定义线程类 class DataReaderThread(threading.Thread): def __init__(self, start, end): super().__init__() self.start = start self.end = end def run(self): # 创建数据库连接 conn = pymysql.connect(**db_config) cursor = conn.cursor() # 查询数据 query = f"SELECT * FROM table_name LIMIT {self.start}, {self.end - self.start}" cursor.execute(query) result = cursor.fetchall() # 处理数据 # ... # 关闭连接 cursor.close() conn.close() # 设置线程数量和数据分块大小 num_threads = 4 chunk_size = 10000 # 创建线程并启动 threads = [] for i in range(num_threads): start = i * chunk_size end = (i + 1) * chunk_size thread = DataReaderThread(start, end) thread.start() threads.append(thread) # 等待所有线程完成 for thread in threads: thread.join() ``` 与多线程相比,多进程可以更好地利用多核处理器的优势,适用于计算密集型任务使用多进程读取数据时,可以使用 `multiprocessing` 模块来创建和管理进程。每个进程独立地读取数据库的一部分数据,并进行相应的处理。需要注意的是,不同进程之间的数据共享需要通过进程间通信机制来实现。 以下是一个简单的示例代码,使用多进程从数据库读取数据: ```python import multiprocessing import pymysql # 设置数据库连接参数 db_config = { 'host': 'localhost', 'user': 'username', 'password': 'password', 'database': 'database_name' } # 定义进程函数 def data_reader(start, end, result_queue): # 创建数据库连接 conn = pymysql.connect(**db_config) cursor = conn.cursor() # 查询数据 query = f"SELECT * FROM table_name LIMIT {start}, {end - start}" cursor.execute(query) result = cursor.fetchall() # 处理数据 # ... # 将结果放入队列 result_queue.put(result) # 关闭连接 cursor.close() conn.close() # 设置进程数量和数据分块大小 num_processes = 4 chunk_size = 10000 # 创建进程池和结果队列 pool = multiprocessing.Pool(processes=num_processes) result_queue = multiprocessing.Queue() # 提交任务到进程池 for i in range(num_processes): start = i * chunk_size end = (i + 1) * chunk_size pool.apply_async(data_reader, (start, end, result_queue)) # 关闭进程池,等待所有任务完成 pool.close() pool.join() # 处理结果队列的数据 while not result_queue.empty(): result = result_queue.get() # 处理数据 # ... ``` 需要根据具体的场景和需求选择使用多线程或多进程。多线程适用于 I/O 密集型任务,而多进程适用于计算密集型任务。在进行数据库读取时,多线程可能更适合,因为数据库的读取操作通常是 I/O 密集型的。然而,在某些情况下,多进程也可以提供更好的性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值