Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/335.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python Foreach循环中的并行处理_Python_Python 2.7 - Fatal编程技术网

Python Foreach循环中的并行处理

Python Foreach循环中的并行处理,python,python-2.7,Python,Python 2.7,你好,我遇到了一个情况,我正在调用一些API来获取电影列表。对于列表中的每条记录,我调用另一个API。为了更好的性能,我想让循环并行。下面是我的示例代码 movies = [] for movie in collection: tmdb_movie = tmdb.get_movie(movie['detail']['ids']['tmdb_id']) movies.append(tmdb_movie) return tmdb_movie 因此,我使用多处理的解决方案如下:

你好,我遇到了一个情况,我正在调用一些API来获取电影列表。对于列表中的每条记录,我调用另一个API。为了更好的性能,我想让循环并行。下面是我的示例代码

movies = []

for movie in collection:
    tmdb_movie = tmdb.get_movie(movie['detail']['ids']['tmdb_id'])
    movies.append(tmdb_movie)

return tmdb_movie
因此,我使用多处理的解决方案如下:

pool = Pool()
output = pool.map(tmdb.get_movie, [movie['detail']['ids']['tmdb_id'] for movie in collection])
但当我执行这段代码时,我得到以下错误

PicklingError:无法pickle:属性查找\内置\实例方法失败
如果有人能帮助我在Python2.7中实现这一功能,我将不胜感激。

最好的选择是使用线程。Python中的线程不能并行使用CPU,但它们可以在有阻塞操作时并发执行。进程虽然可以真正并行运行,但启动和通信速度较慢,更适合于CPU受限的大工作负载。此外,正如您在问题中指出的,流程有时很难启动

您可以使用某种程度上保密的类,即未记录但实际上众所周知的multiprocessing.pool.ThreadPool类。如果要多次这样做,可以在开始时创建一个池并重用它。您只需要确保在程序退出时调用pool.close和pool.join

from multiprocessing.pool import ThreadPool

# Global/class variables    
NUM_THREADS = 5
pool = ThreadPool(NUM_THREADS)

# Inside some function/method
return pool.map(lambda movie: tmdb.get_movie(movie['detail']['ids']['tmdb_id']), movies)

# On exit
pool.close()  # Prevents more jobs to be submitted
pool.join()  # Waits until all jobs are finished

你的问题非常广泛,遗漏了很多细节,所以这里有一个需要做的概要。为了避免PicklingError,在每个进程中都会打开数据库,这可以通过在下面的示例代码中使用名为start_process的初始化函数来完成

注意:由于初始化数据库以执行一个查询所涉及的开销,@jdehesa的多线程方法可能是更好的策略,在这种情况下,线程通常会降低共享全局变量的成本。或者,您可以使get_movie interface函数每次调用时处理多个id,即成批处理

class Database:
    """ Mock implementation. """
    def __init__(self, *args, **kwargs):
        pass  # Open/connect to database.

    def get_movie(self, id):
        return 'id_%s_foobar' % id


import multiprocessing as mp

def start_process(*args):
    global tmdb
    tmdb = Database(*args)

def get_movie(id):
    tmdb_movie = tmdb.get_movie(id)
    return tmdb_movie

if __name__ == '__main__':

    collection = [{'detail': {'ids': {'tmdb_id': 1}}},
                  {'detail': {'ids': {'tmdb_id': 2}}},
                  {'detail': {'ids': {'tmdb_id': 3}}}]

    pool_size = mp.cpu_count()
    with mp.Pool(processes=pool_size, initializer=start_process,
                 initargs=('init info',)) as pool:
        movies = pool.map(get_movie, (movie['detail']['ids']['tmdb_id']
                                        for movie in collection))

    print(movies)  # -> ['id_1_foobar', 'id_2_foobar', 'id_3_foobar']
一种多处理替代方案允许多个进程在某种程度上共享数据库,而无需每次连接数据库。该方案是定义一个自定义项,该自定义项打开数据库一次,并提供一个接口,以获取给定ID的一部或多部电影的信息。在线文档的“服务器进程”小节中也讨论了这一点。内置管理器支持多种容器类型、列表、dict和队列

下面是显示如何为数据库创建自己的自定义管理器的示例代码。如果取消对打印调用的注释,您将看到只创建了一个数据库实例:

class Database:
    """ Mock implementation. """
    def __init__(self, *args, **kwargs):
#        print('Database.__init__')
        pass  # Open/connect to database.

    def get_movie(self, id):
        return 'id_%s_foobar' % id


from functools import partial
import multiprocessing as mp
from multiprocessing.managers import BaseManager


class DB_Proxy(object):
    """ Shared Database instance proxy class. """
    def __init__(self, *args, **kwargs):
        self.database = Database(*args, **kwargs)

    def get_movie(self, id):
#        print('DB_Proxy.get_movie')
        tmdb_movie = self.database.get_movie(id)
        return tmdb_movie


class MyManager(BaseManager): pass  # Custom Manager

MyManager.register('DB_Proxy', DB_Proxy)


if __name__ == '__main__':

    collection = [{'detail': {'ids': {'tmdb_id': 1}}},
                  {'detail': {'ids': {'tmdb_id': 2}}},
                  {'detail': {'ids': {'tmdb_id': 3}}}]

    manager = MyManager()
    manager.start()

    db_proxy = manager.DB_Proxy('init info')

    pool_size = mp.cpu_count()
    with mp.Pool(pool_size) as pool:
        movies = pool.map(db_proxy.get_movie,
                          (movie['detail']['ids']['tmdb_id']
                            for movie in collection))

    print(movies)  # -> ['id_1_foobar', 'id_2_foobar', 'id_3_foobar']

我们不实现特性,我们帮助解决现有代码中的错误和其他问题。有一种方法可以使东西并行。你必须使用Python2.7吗?对于像这样的I/O绑定任务,最好使用asyncio模块,但它仅在Python 3.5+中可用。是的,我的问题是我仅限于Python 2.7,非常感谢。此pool.map函数是否尊重收集的初始排序/顺序?电影收藏在我的example@nafr1是的,它应该给你原始顺序的映射序列。非常感谢你的回复。我最终使用了multiprocessing.pool中的ThreadPool,它在Windows上运行得非常好。然而,我的目标是Android应用程序,我想Android不支持多处理。你能想到其他的解决办法吗?也许直接使用线程。线程?对不起,我对安卓不太了解。无论如何,线程可以在没有ThreadPool的情况下使用,根据其docstring,ThreadPool是一个支持将函数应用于参数的异步版本的类,并且是从multipprocessing.Pool派生的。无论如何,是的,你可能会使用线程。直接线程。您可以使用Queue.Queue在主线程和后台线程之间进行通信,后台线程连续地从输入队列获取内容,查找它们,并将获得的结果放入输出队列,然后主线程可以检索输出队列。