Python multiprocessing.Process()是如何知道开启多少个并发进程的?
我正在运行一个脚本,目的是获取数据库表的列表,检查每个表的行数,然后把每个查询的结果添加到一个字典里。为了加快速度,我使用了多进程:用Manager来创建一个可以共享的列表和字典,这样各个进程就可以读取和添加数据,同时用Process来设置这些进程。
from multiprocessing import Process, Manager
def main():
mgr = Manager()
# Function to get the list of tables
table_list = mgr.list(get_table_list())
counts = mgr.dict()
for table in table_list:
# get_table_count runs a 'SELECT COUNT(*) FROM <table>' and appends
# the result to the counts dict
p = Process(target=select_star, args=(table, counts, 'prod'))
p.start()
p.join()
我想问的是,Process是怎么管理打开的进程数量的?我之前习惯使用Pool()和apply_async()方法,在创建Pool的时候就可以指定进程的数量。
如果你能提供一些关于这种方法是否适合我这个应用的额外意见,我会很感激。
--
感谢dano帮助我实现下面的两个方案:
1 - 使用我习惯的Pool.apply_async:
from multiprocessing import Process, Manager, cpu_count
def main():
mgr = Manager()
table_list = get_table_list()
pool = Pool(cpu_count() / 2)
prod_counts = mgr.dict()
for table in table_list:
pool.apply_async(get_table_count, args=(table, prod_counts, 'prod'))
pool.close()
pool.join()
2 - 使用Pool.map()和itertools.partial()
from multiprocessing import Process, Manager, cpu_count
def main():
mgr = Manager()
table_list = get_table_list()
pool = Pool(cpu_count() / 2)
prod_counts = mgr.dict()
func = partial(get_table_count, result_dict=prod_counts, env='prod')
pool.map(func, table_list)
print prod_counts
2 个回答
0
2
multiprocessing.Process
这个东西并不知道有多少其他的进程在运行,也不会去管理正在运行的 Process
对象的数量。如果你想要这样的功能,就得用 multiprocessing.Pool
。
当你直接使用 Process
的时候,一调用 p.start()
就会立刻启动一个子进程,而当你调用 p.join()
时,就会等待这个 Process
结束。所以在你的示例代码中,你其实一次只在运行一个进程,但你却启动了 len(table_list)
这么多个不同的进程。
这样做并不好;因为你一次只启动一个进程,所以实际上并没有做到并行处理。这样反而会比普通的单线程或单进程方式慢,因为启动子进程和访问 Manager.dict
的开销会更大。你应该直接使用 Pool
来处理:
from functools import partial
from multiprocessing import Manager, Pool
def select_star(table, counts, type_): # counts and type_ will always be the counts dict and "prod", respectively
pass
def main():
mgr = Manager()
counts = mgr.dict()
p = Pool()
func = partial(select_star, counts, "prod") # Using a partial lets us pass extra parameters to select_start
p.map(func, get_table_list()) # No need to use a manager for the list, since you're not passing the whole thing to the children.
if __name__ == "__main__":
main()