Python Foreach循环中的并行处理
你好,我遇到了一个情况,我正在调用一些API来获取电影列表。对于列表中的每条记录,我调用另一个API。为了更好的性能,我想让循环并行。下面是我的示例代码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 因此,我使用多处理的解决方案如下:
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在主线程和后台线程之间进行通信,后台线程连续地从输入队列获取内容,查找它们,并将获得的结果放入输出队列,然后主线程可以检索输出队列。