Python 如何使用单独的线程运行多个shell作业,并在同时执行每个shell作业后等待每个shell作业完成?

Python 如何使用单独的线程运行多个shell作业,并在同时执行每个shell作业后等待每个shell作业完成?,python,multithreading,parallel-processing,subprocess,popen,Python,Multithreading,Parallel Processing,Subprocess,Popen,我正在编写一个需要处理大量数据的脚本。我意识到脚本的并行组件实际上对包含大量单独数据点的实例没有帮助。我将创建临时文件并并行运行它们。我在qsub上运行这个程序,因此我将通过-pe threaded$N_JOBS分配一定数量的线程(在这个小示例中是4) 我的最终目标是使用我分配的一个线程启动每个进程,然后等待所有作业完成后再继续 但是,我只使用过process=subprocess.Popen和process.communicate()来运行shell作业。我过去在使用process.wait(

我正在编写一个需要处理大量数据的脚本。我意识到脚本的并行组件实际上对包含大量单独数据点的实例没有帮助。我将创建临时文件并并行运行它们。我在
qsub
上运行这个程序,因此我将通过
-pe threaded$N_JOBS
分配一定数量的线程(在这个小示例中是4)

我的最终目标是使用我分配的一个线程启动每个进程,然后等待所有作业完成后再继续

但是,我只使用过
process=subprocess.Popen
process.communicate()
来运行shell作业。我过去在使用
process.wait()
时遇到过一些问题,因为僵尸进程

如何修改我的
run
函数以启动作业,而不是等待完成,然后启动下一个作业,然后在所有作业运行后,等待所有作业完成?

请让我知道,如果这是不清楚,我可以更好地解释。在下面的示例中(可能是一个糟糕的示例?),我想使用4个单独的线程(我不知道如何设置b/c,我只做过
joblib.Parallel
以实现简单的并行化),其中每个线程运行命令
echo'$thread'&&sleep 1
。因此,最终需要1秒多一点,而不是4秒

我找到了这篇文章:但我不确定如何用
运行
脚本来适应我的情况

import sys, subprocess, time 

# Number of jobs
N_JOBS=4

# Run command
def run(
    cmd,
    popen_kws=dict(),
    ):

    # Run
    f_stdout = subprocess.PIPE
    f_stderr = subprocess.PIPE

    # Execute the process
    process_ = subprocess.Popen(cmd, shell=True, stdout=f_stdout, stderr=f_stderr, **popen_kws) 
    # Wait until process is complete and return stdout/stderr
    stdout_, stderr_ = process_.communicate() # Use this .communicate instead of .wait to avoid zombie process that hangs due to defunct. Removed timeout b/c it's not available in Python 2

    # Return code
    returncode_ = process_.returncode

    return {"process":process_, "stdout":stdout_, "stderr":stderr_, "returncode":returncode_}

# Commands
cmds = list(map(lambda x:"echo '{}' && sleep 1".format(x), range(1, N_JOBS+1)))
# ["echo '1'", "echo '2'", "echo '3'", "echo '4'"]

# Start time 
start_time = time.time()
results = dict()
for thread, cmd in enumerate(cmds, start=1):
    # Run command but don't wait for it to finish (Currently, it's waiting to finish)
    results[thread] = run(cmd)

# Now wait until they are all finished
print("These jobs took {} seconds\n".format(time.time() - start_time))
print("Here's the results:", *results.items(), sep="\n")
print("\nContinue with script. .. ...")

# These jobs took 4.067937850952148 seconds

# Here's the results:
# (1, {'process': <subprocess.Popen object at 0x1320766d8>, 'stdout': b'1\n', 'stderr': b'', 'returncode': 0})
# (2, {'process': <subprocess.Popen object at 0x1320547b8>, 'stdout': b'2\n', 'stderr': b'', 'returncode': 0})
# (3, {'process': <subprocess.Popen object at 0x132076ba8>, 'stdout': b'3\n', 'stderr': b'', 'returncode': 0})
# (4, {'process': <subprocess.Popen object at 0x132076780>, 'stdout': b'4\n', 'stderr': b'', 'returncode': 0})

# Continue with script. .. ...

你快到了。处理多处理的最简单方法是使用
multiprocessing.Pool
对象,如介绍、然后或您的函数集中所示。
map()
starmap()
之间的最大区别在于
map()
假设函数接受一个参数(因此可以传递一个简单的iterable),而
starmap()
需要嵌套的iterable参数

对于您的示例,这将起作用(
run()
函数基本上被跳过,尽管我将签名更改为命令和参数列表,因为将字符串传递给系统调用通常不是一个好主意):


没有必要拥有与命令相同数量的作业;
中的子进程将根据需要重新使用以运行函数。

使用
星图
我得到以下错误:
#类型错误:run()从1到3个位置参数中获取,但给出了21个
映射
我得到了
可能是编码错误:错误发送结果:'[{'process':,'stdout':b'3\n','stderr':b','returncode':0}]'。原因:'TypeError(“无法pickle_thread.lock对象”),)“
您正在使用原始的
run
函数,该函数需要1个必需的参数。使用
map
,您永远无法传递2个参数,因此无法传递popen_kwds,但它会起作用。
starmap
需要嵌套列表(例如
['ls-l',{'shell':True}],'pwd,…])
)。返回只能传递可pickle的对象,因此Popen对象不能作为返回。只需返回stdout、stdin和returncode值,它们就可以pickle了。
# Run command
def run(
    cmd,
    errors_ok=False,
    popen_kws=dict(),
    ):

    # Run
    f_stdout = subprocess.PIPE
    f_stderr = subprocess.PIPE

    # Execute the process
    process_ = subprocess.Popen(cmd, shell=True, stdout=f_stdout, stderr=f_stderr, **popen_kws) 

    return process_

# Commands
cmds = list(map(lambda x:"echo '{}' && sleep 0.5".format(x), range(1, N_JOBS+1)))
# ["echo '1'", "echo '2'", "echo '3'", "echo '4'"]

# Start time 
start_time = time.time()
results = dict()
for thread, cmd in enumerate(cmds, start=1):
    # Run command but don't wait for it to finish (Currently, it's waiting to finish)
    p = multiprocessing.Process(target=run, args=(cmd,))
    p.start()
    p.join()
    results[thread] = p
from multiprocessing import Pool

N_JOBS = 4

def run(cmd, *args):
    return cmd + str(args)

cmds = [
    ('echo', 'hello', 1, 3, 4),
    ('ls', '-l', '-r'),
    ('sleep', 3),
    ('pwd', '-P'),
    ('whoami',),
]

results = []
with Pool(N_JOBS) as p:
    results = p.starmap(run, cmds)

for r in results:
    print(r)