Python 为什么多线程不能加速我的程序?

Python 为什么多线程不能加速我的程序?,python,multithreading,python-multiprocessing,python-multithreading,Python,Multithreading,Python Multiprocessing,Python Multithreading,我有一个大的文本文件需要处理。我首先将所有文本读入一个列表,然后使用ThreadPoolExecutor启动多个线程来处理它。此处未列出在process_text()中调用的两个函数:is_channel和get_relations() 我在Mac上,我的观察表明它并没有真正加快处理速度(cpu有8个内核,只使用了15%的cpu)。如果函数is_channel或get_关系中存在性能瓶颈,那么多线程将不会有多大帮助。这就是没有绩效提升的原因吗?我是否应该尝试使用多处理来提高速度,而不是多线程 d

我有一个大的文本文件需要处理。我首先将所有文本读入一个列表,然后使用ThreadPoolExecutor启动多个线程来处理它。此处未列出在process_text()中调用的两个函数:is_channel和get_relations()

我在Mac上,我的观察表明它并没有真正加快处理速度(cpu有8个内核,只使用了15%的cpu)。如果函数is_channel或get_关系中存在性能瓶颈,那么多线程将不会有多大帮助。这就是没有绩效提升的原因吗?我是否应该尝试使用多处理来提高速度,而不是多线程

def process_file(file_name):
    all_lines = []
    with open(file_name, 'r', encoding='utf8') as f:
        for index, line in enumerate(f):
            line = line.strip()
            all_lines.append(line)
    
    # Classify text
    all_results = []
    with ThreadPoolExecutor(max_workers=10) as executor:
        for index, result in enumerate(executor.map(process_text, all_lines, itertools.repeat(channel))):
           all_results.append(result)

    for index, entities_relations_list in enumerate(all_results):
        # print out results

def process_text(text, channel):
    global channel_text
    global non_channel_text
    
    is_right_channel = is_channel(text, channel)

    entities = ()
    relations = None
    entities_relations_list = set()
    entities_relations_list.add((entities, relations))
    if is_right_channel:
        channel_text += 1
        entities_relations_list = get_relations(text, channel)
        return (text, entities_relations_list, is_right_channel)
    non_channel_text += 1
    return (text, entities_relations_list, is_right_channel)

首先要做的是找出需要多少时间:

  • 读取内存中的文件(T1)
  • 执行所有处理(T2)
  • 打印结果(T3)
  • 第三点(打印),如果你真的这样做,会减慢速度。只要您不将其打印到终端,而只是将输出传输到文件或其他内容,就可以了

    根据时间安排,我们将了解:

    • T1>>T2=>IO绑定
    • T2>>T1=>CPU受限
    • T1和T2接近=>两者都不接近。
      通过
      x>>y
      我的意思是x明显大于y
    基于上述内容和文件大小,您可以尝试以下几种方法:

    基于线程的 即使这样,也可以通过两种方式完成,通过再次基准测试/查看时间安排,可以找到哪种方式工作得更快

    方法-1(T1>>T2或甚至在T1和T2相似时)

    • 运行代码以在线程中读取文件本身,并让它将行推送到
      队列
      ,而不是列表。
      此线程在从文件读取完毕后,在末尾插入一个None。这对于告诉工人他们可以停止工作很重要
    • 现在运行处理人员并将队列传递给他们
    • 工人们在循环中不断地从队列中读取数据并处理结果。与读取器线程类似,这些工作人员将结果放入队列中。 一旦线程遇到None,它将停止循环并将None重新插入队列(以便其他线程可以自行停止)
    • 打印部分也可以在一根线中完成
    上面是单生产者线程和多消费者线程的示例

    方法2(这只是问题中代码片段已经完成的另一种方式)

    • 将整个文件读取到列表中
    • 根据线程数将列表划分为索引范围。
      示例:如果文件总共有100行,我们使用10个线程
      然后是0-9,10-19。。。。90-99是索引范围
      将完整列表和这些索引范围传递给线程以处理每个集合。由于您没有修改原始列表,因此此操作有效。
      这种方法比为每一行运行worker效果更好
    基于多处理的 (CPU限制)

    • 处理前将文件拆分为多个文件
    • 为每个文件运行一个新进程
    • 每个进程获取它应该读取和处理的文件的路径
    • 这需要在末尾合并所有结果/文件的附加步骤
      流程创建部分可以在python中使用
      multiprocessing
      module
      或者从驱动程序脚本为每个文件生成python进程,如shell脚本
    只要看一下代码,它似乎是CPU受限的。因此,我更喜欢多处理。我在实践中使用了这两种方法

    • 多处理:当处理存储在磁盘上的巨大文本文件(GBs)时(就像您正在做的)
    • 线程(方法1):从多个数据库读取数据时。因为这比CPU更受IO限制(我使用了多个生产者线程和多个消费者线程)

    假设您的Python程序受CPU限制,您可能会看到cPython GIL的结果:。。。在这种情况下,切换到多处理可能会有所帮助。如果您的程序不受CPU限制,那么它可能是受I/O限制的,在这种情况下,性能会受到硬盘速度的限制,因此多个进程可能没有帮助。@删减答案没有解释性能差异的原因,它只是描述了差异(线程共享同一内存,进程不共享)。您需要使用互斥来调解对
    频道文本
    非频道文本
    @Prune的访问。他正在使用共享变量
    频道文本
    非频道文本
    。多进程将使其更加复杂,需要与主进程进行进程间通信。@marlon Yes。递增变量不是原子的。看见