为什么在使用Python多处理池时会有空闲的工作线程?

为什么在使用Python多处理池时会有空闲的工作线程?,python,parallel-processing,python-multiprocessing,Python,Parallel Processing,Python Multiprocessing,我将一个非常大的文本文件分解成更小的块,并对这些块执行进一步的处理。对于本例,让text\u chunks成为一个列表列表,每个列表包含一段文本。文本块的元素长度从~50到~15000不等。类ProcessedText存在于代码中的其他位置,并根据提供给它的文本进行大量后续处理和数据分类。使用如下代码将不同的文本块并行处理为ProcessedText实例: def do_things_to_text(a, b): #pull out necessary things for Proces

我将一个非常大的文本文件分解成更小的块,并对这些块执行进一步的处理。对于本例,让
text\u chunks
成为一个列表列表,每个列表包含一段文本。
文本块的元素长度从~50到~15000不等。类
ProcessedText
存在于代码中的其他位置,并根据提供给它的文本进行大量后续处理和数据分类。使用如下代码将不同的文本块并行处理为
ProcessedText
实例:

def do_things_to_text(a, b):
    #pull out necessary things for ProcessedText initialization and return an instance
    print('Processing {0}'.format(a))
    return ProcessedText(a, b)

import multiprocessing as mp

#prepare inputs for starmap, pairing with list index so order can be reimposed later
pool_inputs = list(enumerate(text_chunks))

#parallel processing
pool = mp.Pool(processes=8)
results = pool.starmap_async(do_things_to_text, pool_inputs)
output = results.get()
代码成功执行,但似乎在代码运行时,作为
的一部分创建的一些工作进程随机处于空闲状态。在代码执行时,我在
top
中跟踪内存使用情况、CPU使用情况和状态

在开始时,所有8个工作进程都被占用(状态“R”在
top
中,CPU使用率为非零),当
text\u块中的约20个条目完成后,工作进程开始变化很大。有时,只有一个工作进程正在运行,而其他进程处于“S”状态,CPU使用率为零。我还可以从打印的输出语句中看到,
do\u things\u to\u text()
被调用的频率较低。到目前为止,我还无法确定进程开始空闲的原因。还有很多条目需要处理,因此它们闲置会导致时间效率低下

我的问题是:

  • 为什么这些工作进程处于空闲状态
  • 有没有更好的方法来实现
    多处理
    来防止这种情况
  • 编辑以添加: 我进一步描述了这个问题。从我在
    do_things\u to_text()
    中打印的索引中可以清楚地看出,多处理是将作业总数按每十个索引划分为线程。因此,我的控制台输出显示作业0、10、20、30、40、50、60、70同时提交(8个进程)。有些作业完成得比其他作业快,因此您可能会在看到作业1完成之前看到作业22已完成


    在第一批线程完成之前,所有进程都处于活动状态,没有空闲。但是,当该批处理完成且作业80启动时,只有一个进程处于活动状态,其他7个进程处于空闲状态。我还没有确认,但我相信在80系列完成之前,它会一直保持这种状态。

    以下是一些提高内存利用率的建议:

    我不知道如何创建
    文本块
    ,但最终在
    池输入中得到了8GB的字符串。理想情况下,您应该有一个生成器函数,例如,
    make_text_chunks
    ,该函数生成以前包含
    text_chunks
    iterable的单个“text chunks”(如果
    text_chunks
    已经是这样一个生成器表达式,则您都已设置好)。这样做的目的不是一次性创建所有8GB的数据,而是仅在需要数据时创建。使用此策略,您不能再使用
    Pool
    方法
    starmap\u asynch
    ;我们将使用。与
    startmap\u asynch
    不同,此方法将以
    chunksize
    块的形式迭代提交作业,您可以在结果可用时对其进行处理(尽管这似乎不是问题)


    文本文件有多大?提交了多少作业,即
    len(池输入)
    ?进程空闲多长时间?@Booboo文本文件大小会有所不同,但对于我目前正在开发的示例,大约为8GB。pool_输入目前为301个元素,但在一般应用中也可能有所不同。我认为这些进程可能会空闲5-10分钟,而一个进程在不空闲时通常会在1分钟左右完成。我也有一些新的发现,我将用它们来更新原来的问题;我只能提出一些更好地利用记忆的建议。他们是否会解决你所有的问题是一个大问号。@Booboo欢迎所有的建议!一些评论:(1)请参阅下面我的答案中的
    compute\u chunksize
    函数。这就是
    starmap\u asynch
    将用于根据池的大小和
    pool\u输入的大小将
    pool\u输入分解为“块”的函数。例如,给定
    pool\u输入中的池大小为8和300个元素,将计算
    chunksize
    值10。因此,它将把
    pool_inputs
    的前10个元素放在第一个进程的输入队列上,接下来的10个元素放在第二个进程的输入队列上。这就是为什么您会看到初始作业打印为0、10、20等(更多…)谢谢!对于这个问题,这当然值得考虑,但不幸的是,在当前迭代中,必须将块读入内存,因为我必须以固定的间隔拆分原始文本文件,然后重新组合所有内容,以使其按分隔符拆分。我会记住,在将来重新写入时,只需将文本文件读入内存,并从一开始进行分隔符拆分,这将允许我
    imap
    def make_text_chunks():
        # logic goes here to generate the next chunk
        yield text_chunk
    
    
    def do_things_to_text(t):
        # t is now a tuple:
        a, b = t
        #pull out necessary things for ProcessedText initialization and return an instance
        print('Processing {0}'.format(a))
        return ProcessedText(a, b)
    
    
    import multiprocessing as mp
    
    # do not turn into a list!
    pool_inputs = enumerate(make_text_chunks())
    
    def compute_chunksize(n_jobs, poolsize):
        """
        function to compute chunksize as is done by Pool module
        """
        if n_jobs == 0:
            return 0
        chunksize, remainder = divmod(n_jobs, poolsize * 4)
        if remainder:
            chunksize += 1
        return chunksize
    
    #parallel processing
    # number of jobs approximately
    # don't know exactly without turning pool_inputs into a list, which would be self-defeating
    N_JOBS = 300
    POOLSIZE = 8
    CHUNKSIZE = compute_chunksize(N_JOBS, POOLSIZE)
    with mp.Pool(processes=POOLSIZE) as pool:
        output = [result for result in pool.imap(do_things_to_text, pool_inputs, CHUNKSIZE)]