【问题标题】:Using multiprocessing in python to return values在python中使用多处理返回值
【发布时间】:2021-07-27 04:13:32
【问题描述】:

背景

我现在有一些看起来像这样的代码。

failed_player_ids: Set[str] = set()
for player_id in player_ids:
    success = player_api.send_results(
        player_id, user=user, send_health_results=True
    )
    if not success:
        failed_player_ids.add(player_id)

此代码运行良好,但问题是每次调用需要 5 秒。每分钟有 2000 个呼叫的速率限制,所以我远远低于最大容量。我想并行化它以加快速度。这是我第一次在 python 中使用multiprocessing 库,因此我对应该如何进行有点困惑。我可以用语言描述我想做什么。

在我当前的代码中,我循环遍历 player_id 列表,如果 api 响应成功,我什么也不做,如果失败,我记下该玩家 ID。

我不确定如何实现此代码的并行版本。我有一些想法,但我有点困惑。

这就是我目前的想法

from multiprocessing import Pool


    
    num_processors_to_use = 5 # This is a number can be increased to get more speed
    
    def send_player_result(player_id_list: List[str]) -> Optional[str]:
        for player_id in player_id_list:
            success = player_api.send_results(player_id, user=user, send_health_results=True)
            if not success:
                return player_id
    # Caller
    with Pool(processes=num_processors_to_use) as pool:
            responses = pool.map(
                func=send_player_result,
                iterable=player_id_list,
            )
            failed_player_ids = Set(responses)

 

任何 cmets 和建议都会有所帮助。

【问题讨论】:

  • @joanis 谢谢你的第一篇文章是一个很好的发现。如果您能用您的评论解释/纠正我在上面发布的答案,如果对您来说不太困难/耗时,我也将不胜感激。我觉得它可以帮助我更好地理解。
  • 你写的东西实际上是做什么的?它是否有效?
  • PS:我还没有在 Python 中做过多处理器的事情,我只是意识到最近有一些关于它的问题。希望这里的其他人能够对您的代码发表评论,如果您指出它尚未以何种方式工作。

标签: python python-multiprocessing


【解决方案1】:

如果您使用函数map,则iterable player_id_list 的每个item 将作为单独的任务传递给函数send_player_result。因此,此函数不应再期望传递玩家 id 列表,而是传递单个玩家 id。而且,正如您现在所知道的,如果您的任务主要受 I/O 限制,那么多线程是一个更好的模型。您可以:

from multiprocessing.dummy import Pool
# or
from multiprocessing.pool import ThreadPool

您可能希望大大增加线程数(但不要大于player_id_list 的大小):

#from multiprocessing import Pool
from multiprocessing.dummy import Pool
from typing import Set

def send_player_result(player_id):
    success = player_api.send_results(player_id, user=user, send_health_results=True)
    return success

# Only required for Windows if you are doing multiprocessing:
if __name__ == '__main__':
    
    pool_size = 5 # This is a number can be increased to get more concurrency
    
    # Caller
    failed_player_ids: Set[str] = set()
    with Pool(pool_size) as pool:
        results = pool.map(func=send_player_result, iterable=player_id_list)
        for idx, success in enumerate(results):
            if not success:
                # failed for argument player_id_list[idx]:
                failed_player_ids.add(player_id_list[idx])
            

【讨论】:

  • 感谢您的精彩解释。我有几个后续问题。 from multiprocessing.dummy import Pool 和做 from multiprocessing import Pool 有什么区别最后,如果我决定使用多线程,那么我会使用你提供的后一个选项 from multiprocessing.pool import ThreadPool 吗??
  • 我在post找到了第一个问题的答案
  • 我会不会受到限制,也不会通过使用 .dummy 来实现任何并行模拟?还有我的原始解决方案,我循环遍历每个玩家 ID 并进行 api 调用,每次调用需要 5 秒。假设现在我每 5 秒拨打 5 次电话,因此我基本上每分钟拨打 60 次电话,是否安全?
  • from multiprocessing.dummy import Pool 等价于from multiprocessing.pool import ThreadPool 并使用多线程而不是多处理(在多线程情况下,底层类实际上是multiprocessing.pool.ThreadPool,在多处理情况下是multiprocessing.pool.Pool。如果您的任务是主要是做 I/O (比如发布到一个 URL) 你的任务大部分时间都处于等待状态并且多线程的已知问题, 即锁定 GIL 并不是什么大问题。线程的创建和创建成本要低得多更容易使用。(更多)
  • 如果你的工作函数是 CPU 密集型的,那么你需要from multiprocessing import Poolfrom multiprocessing.pool import Pool(你最终得到相同的结果)。我无法对您的吞吐量做出任何预测。我什至不知道您的工作函数在做什么,也不知道它是真正的 I/O 还是 CPU 密集型。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-01
  • 1970-01-01
  • 2021-10-15
  • 2015-08-25
相关资源
最近更新 更多