Python multiprocessing.Process()是如何知道开启多少个并发进程的?

0 投票
2 回答
5416 浏览
提问于 2025-04-18 14:18

我正在运行一个脚本,目的是获取数据库表的列表,检查每个表的行数,然后把每个查询的结果添加到一个字典里。为了加快速度,我使用了多进程:用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

来自文档

在多进程编程中,进程是通过创建一个进程对象,然后调用它的start()方法来启动的。

简单来说,它并不管理打开的进程数量。你只要调用start(),就会启动一个新的进程。

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()

撰写回答