【问题标题】:Is there a multithreaded map() function? [closed]是否有多线程 map() 函数? [关闭]
【发布时间】:2010-04-01 18:40:07
【问题描述】:

我有一个没有副作用的功能。我想为数组中的每个元素运行它并返回一个包含所有结果的数组。

Python 是否可以生成所有值?

【问题讨论】:

  • 你的意思是 map 和 map-reduce 一样吗?你能举一个输入和输出行的例子吗?
  • 不,我不是说 map reduce。因为我希望返回每个单独函数的所有数据。只是每个值都可以相互独立地计算。虽然,因为我认为我想要集合的最大值,也许我可以在这里使用 map reduce...
  • map() 会按照你说的做,独立操作每个元素(下面提到的 GIL 警告)
  • streams.fastmap() from Pyxtension: github.com/asuiu/pyxtension 正是这样做的——多线程映射。

标签: python multithreading


【解决方案1】:

尝试多处理中的 Pool.map 函数:

http://docs.python.org/library/multiprocessing.html#using-a-pool-of-workers

它本身不是多线程的,但这实际上很好,因为 GIL 在 Python 中严重削弱了多线程。

【讨论】:

  • 非常酷,最后一行卖给了我。我以前看过这个多处理库,但我认为它对我的需求来说太重了。我想我现在看到了曙光 :) 谢谢。
  • 这个答案是否是最新的/仍然与多线程/GIL 相关?
  • 是的,对于“严重”的某些值。我认为这是一个见仁见智的问题,但我仍然更喜欢 Linux 上的多个进程,其中进程不太重。
【解决方案2】:

在 Python 标准库(3.2 版中的新功能)中尝试concurrent.futures.ThreadPoolExecutor.map

类似于map(func, *iterables),除了:

  • iterables 被立即收集而不是延迟收集;
  • func 是异步执行的,可以同时对 func 进行多次调用。

一个简单的例子(修改自ThreadPoolExecutor Example):

import concurrent.futures
import urllib.request

URLS = [
  'http://www.foxnews.com/',
  'http://www.cnn.com/',
  'http://europe.wsj.com/',
  'http://www.bbc.co.uk/',
]

# Retrieve a single page and report the URL and contents
def load_url(url, timeout):
    # Do something here
    # For example
    with urllib.request.urlopen(url, timeout=timeout) as conn:
      try:
        data = conn.read()
      except Exception as e:
        # You may need a better error handler.
        return b''
      else:
        return data

# We can use a with statement to ensure threads are cleaned up promptly
with concurrent.futures.ThreadPoolExecutor(max_workers=20) as executor:
    # map
    l = list(executor.map(lambda url: load_url(url, 60), URLS))

print('Done.')

【讨论】:

    【解决方案3】:

    您可以使用多处理 python 包 (http://docs.python.org/library/multiprocessing.html)。可从 PiCloud (http://www.picloud.com) 获得的云 python 包还提供了一个多处理 map() 函数,可以将您的地图卸载到云端。

    【讨论】:

      【解决方案4】:

      Python 现在有 concurrent.futures 模块,这是让 map 与多线程或多进程一起工作的最简单方法。

      https://docs.python.org/3/library/concurrent.futures.html

      【讨论】:

        【解决方案5】:

        下面是我的map_parallel 函数。它就像map 一样工作,除了它可以在单独的线程中并行运行每个元素(但请参阅下面的注释)。此答案基于another SO answer

        import threading
        import logging
        def map_parallel(f, iter, max_parallel = 10):
            """Just like map(f, iter) but each is done in a separate thread."""
            # Put all of the items in the queue, keep track of order.
            from queue import Queue, Empty
            total_items = 0
            queue = Queue()
            for i, arg in enumerate(iter):
                queue.put((i, arg))
                total_items += 1
            # No point in creating more thread objects than necessary.
            if max_parallel > total_items:
                max_parallel = total_items
        
            # The worker thread.
            res = {}
            errors = {}
            class Worker(threading.Thread):
                def run(self):
                    while not errors:
                        try:
                            num, arg = queue.get(block = False)
                            try:
                                res[num] = f(arg)
                            except Exception as e:
                                errors[num] = sys.exc_info()
                        except Empty:
                            break
        
            # Create the threads.
            threads = [Worker() for _ in range(max_parallel)]
            # Start the threads.
            [t.start() for t in threads]
            # Wait for the threads to finish.
            [t.join() for t in threads]
        
            if errors:
                if len(errors) > 1:
                    logging.warning("map_parallel multiple errors: %d:\n%s"%(
                        len(errors), errors))
                # Just raise the first one.
                item_i = min(errors.keys())
                type, value, tb = errors[item_i]
                # Print the original traceback
                logging.info("map_parallel exception on item %s/%s:\n%s"%(
                    item_i, total_items, "\n".join(traceback.format_tb(tb))))
                raise value
            return [res[i] for i in range(len(res))]
        

        注意:要注意的一件事是例外。与正常的map 一样,如果上述函数的其中一个子线程引发异常,则上述函数引发异常,并将停止迭代。但是,由于并行性质,不能保证最早的元素会引发第一个异常。

        【讨论】:

          【解决方案6】:

          也许试试Unladen Swallow Python 3 实现?这可能是一个重大项目,不能保证稳定,但如果你愿意,它可以工作。那么list or set comprehensions 似乎是合适的函数结构。

          【讨论】:

            猜你喜欢
            • 2022-09-23
            • 1970-01-01
            • 2011-04-16
            • 1970-01-01
            • 2022-12-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-08-06
            相关资源
            最近更新 更多