我有一个Python脚本,我想用它作为另一个Python脚本的控制器。我有一个64位处理器的服务器,所以想要产生第二个Python脚本的64个子进程。子脚本称为:
$ python create_graphs.py --name=NAME
其中NAME类似于XYZ,ABC,NYU等。
在我的父控制器脚本中,我从列表中检索名称变量:
my_list = [ 'XYZ', 'ABC', 'NYU' ]
所以我的问题是,作为孩子产生这些过程的最佳方法是什么?我想一次将子项数限制为64,所以需要跟踪状态(如果子进程已经完成),这样我就可以有效地保持整个代的运行。
我研究过使用子进程包,但拒绝它,因为它一次只生成一个子进程。我终于找到了多处理器包,但我承认被整个线程与子进程文档所淹没。
现在,我的脚本使用subprocess.call
一次只生成一个孩子,看起来像这样:
#!/path/to/python
import subprocess, multiprocessing, Queue
from multiprocessing import Process
my_list = [ 'XYZ', 'ABC', 'NYU' ]
if __name__ == '__main__':
processors = multiprocessing.cpu_count()
for i in range(len(my_list)):
if( i < processors ):
cmd = ["python", "/path/to/create_graphs.py", "--name="+ my_list[i]]
child = subprocess.call( cmd, shell=False )
我真的希望它一次产生64个孩子。在其他stackoverflow问题中,我看到有人使用Queue,但这似乎会造成性能损失?
答案 0 :(得分:60)
您正在寻找的是多处理中的process pool类。
import multiprocessing
import subprocess
def work(cmd):
return subprocess.call(cmd, shell=False)
if __name__ == '__main__':
count = multiprocessing.cpu_count()
pool = multiprocessing.Pool(processes=count)
print pool.map(work, ['ls'] * count)
这是一个让人们更容易理解的计算示例。以下将在N个进程上划分10000个任务,其中N是cpu计数。请注意,我正在传递None作为进程数。这将导致Pool类使用cpu_count作为进程数(reference)
import multiprocessing
import subprocess
def calculate(value):
return value * 10
if __name__ == '__main__':
pool = multiprocessing.Pool(None)
tasks = range(10000)
results = []
r = pool.map_async(calculate, tasks, callback=results.append)
r.wait() # Wait on the results
print results
答案 1 :(得分:2)
根据Nadia和Jim的评论,这是我提出的解决方案。我不确定它是否是最佳方式,但它确实有效。被调用的原始子脚本需要是一个shell脚本,因为我需要使用一些第三方应用程序,包括Matlab。所以我不得不把它从Python中取出并用bash编码。
import sys
import os
import multiprocessing
import subprocess
def work(staname):
print 'Processing station:',staname
print 'Parent process:', os.getppid()
print 'Process id:', os.getpid()
cmd = [ "/bin/bash" "/path/to/executable/create_graphs.sh","--name=%s" % (staname) ]
return subprocess.call(cmd, shell=False)
if __name__ == '__main__':
my_list = [ 'XYZ', 'ABC', 'NYU' ]
my_list.sort()
print my_list
# Get the number of processors available
num_processes = multiprocessing.cpu_count()
threads = []
len_stas = len(my_list)
print "+++ Number of stations to process: %s" % (len_stas)
# run until all the threads are done, and there is no data left
for list_item in my_list:
# if we aren't using all the processors AND there is still data left to
# compute, then spawn another thread
if( len(threads) < num_processes ):
p = multiprocessing.Process(target=work,args=[list_item])
p.start()
print p, p.is_alive()
threads.append(p)
else:
for thread in threads:
if not thread.is_alive():
threads.remove(thread)
这看起来像是一个合理的解决方案吗?我尝试使用Jim的while循环格式,但我的脚本什么都没有返回。我不确定为什么会这样。这是我用Jim的'while'循环替换'for'循环运行脚本时的输出:
hostname{me}2% controller.py
['ABC', 'NYU', 'XYZ']
Number of processes: 64
+++ Number of stations to process: 3
hostname{me}3%
当我使用'for'循环运行它时,我得到了更有意义的东西:
hostname{me}6% controller.py
['ABC', 'NYU', 'XYZ']
Number of processes: 64
+++ Number of stations to process: 3
Processing station: ABC
Parent process: 1056
Process id: 1068
Processing station: NYU
Parent process: 1056
Process id: 1069
Processing station: XYZ
Parent process: 1056
Process id: 1071
hostname{me}7%
所以这很有效,我很高兴。但是,我仍然不明白为什么我不能使用Jim的'while'样式循环而不是我正在使用的'for'循环。感谢所有的帮助 - 我对@ stackoverflow的广泛知识印象深刻。
答案 2 :(得分:1)
我肯定会使用multiprocessing而不是使用子流程来推广我自己的解决方案。
答案 3 :(得分:1)
我不认为你需要排队,除非你打算从应用程序中获取数据(如果你确实需要数据,我认为无论如何将它添加到数据库可能更容易)
但请尝试以下尺寸:
将create_graphs.py脚本的内容全部放入名为“create_graphs”
的函数中import threading
from create_graphs import create_graphs
num_processes = 64
my_list = [ 'XYZ', 'ABC', 'NYU' ]
threads = []
# run until all the threads are done, and there is no data left
while threads or my_list:
# if we aren't using all the processors AND there is still data left to
# compute, then spawn another thread
if (len(threads) < num_processes) and my_list:
t = threading.Thread(target=create_graphs, args=[ my_list.pop() ])
t.setDaemon(True)
t.start()
threads.append(t)
# in the case that we have the maximum number of threads check if any of them
# are done. (also do this when we run out of data, until all the threads are done)
else:
for thread in threads:
if not thread.isAlive():
threads.remove(thread)
我知道这将导致比处理器少1个线程,这可能是好的,它留下了一个处理器来管理线程,磁盘i / o和计算机上发生的其他事情。如果您决定要使用最后一个核心,只需添加一个核心
编辑:我想我可能误解了my_list的目的。您根本不需要my_list
来跟踪线程(因为它们都被threads
列表中的项引用)。但这是一个很好的方式来输入过程输入 - 甚至更好:使用生成器函数;)
my_list
和threads
my_list
包含您需要在功能中处理的数据
threads
只是当前正在运行的线程的列表
while循环执行两项操作,启动新线程来处理数据,并检查是否有任何线程正在运行。
因此,只要你有(a)要处理的数据,或(b)未完成运行的线程......你想要编程继续运行。 一旦两个列表都为空,它们将评估为False
,而while循环将退出