python multithreading等到所有线程都完成了

Inb*_*ose 98 python multithreading

这可能是在类似的背景下提出的,但是在搜索约20分钟后我无法找到答案,所以我会问.

我编写了一个Python脚本(比方说:scriptA.py)和一个脚本(比如说scriptB.py)

在scriptB中我想用不同的参数多次调用scriptA,每次运行大约需要一个小时,(它是一个巨大的脚本,做了很多东西......不用担心它)我希望能够运行scriptA同时具有所有不同的参数,但我需要等到所有这些都完成后再继续; 我的代码:

import subprocess

#setup
do_setup()

#run scriptA
subprocess.call(scriptA + argumentsA)
subprocess.call(scriptA + argumentsB)
subprocess.call(scriptA + argumentsC)

#finish
do_finish()
Run Code Online (Sandbox Code Playgroud)

我想同时运行所有subprocess.call(),然后等到它们全部完成,我该怎么做?

我试着像这里的例子一样使用线程:

from threading import Thread
import subprocess

def call_script(args)
    subprocess.call(args)

#run scriptA   
t1 = Thread(target=call_script, args=(scriptA + argumentsA))
t2 = Thread(target=call_script, args=(scriptA + argumentsB))
t3 = Thread(target=call_script, args=(scriptA + argumentsC))
t1.start()
t2.start()
t3.start()
Run Code Online (Sandbox Code Playgroud)

但我不认为这是对的.

在我去之前我怎么知道他们都跑完了do_finish()

Aar*_*lla 154

将线程放在列表中,然后使用Join方法

 threads = []

 t = Thread(...)
 threads.append(t)

 ...repeat as often as necessary...

 # Start all threads
 for x in threads:
     x.start()

 # Wait for all of them to finish
 for x in threads:
     x.join()
Run Code Online (Sandbox Code Playgroud)

  • 我不喜欢将列表理解用于它的副作用,而不是对结果列表做任何有用的事情.即使它传播两行,一个简单的for循环也会更干净...... (17认同)
  • 是的,这可行,但更难理解。您应该始终尝试在紧凑的代码和“可读性”之间找到平衡。请记住:代码编写一次,但读取多次。所以简单易懂更重要。 (3认同)
  • "工厂模式"不是我能用一句话解释的东西.谷歌为它和搜索stackoverflow.com.有很多例子和解释.简而言之:您编写的代码可以为您构建复杂的代码.就像一个真正的工厂:你订购并获得成品. (2认同)
  • @Aaron DIgull 我明白这一点。我的意思是我只会在线程中执行 `for x: x.join()` 而不是使用列表理解 (2认同)
  • @IoanAlexandruCucu:我仍然想知道是否有更可读和更有效的解决方案:http://stackoverflow.com/questions/21428602/how-inefficient-is-a-list-compressive-if-you-dont-assign-它 (2认同)

Mak*_*zin 131

您需要在脚本的末尾使用object的join方法Thread.

t1 = Thread(target=call_script, args=(scriptA + argumentsA))
t2 = Thread(target=call_script, args=(scriptA + argumentsB))
t3 = Thread(target=call_script, args=(scriptA + argumentsC))

t1.start()
t2.start()
t3.start()

t1.join()
t2.join()
t3.join()
Run Code Online (Sandbox Code Playgroud)

因此主线程将等待t1,t2t3完成执行.

  • 对"join"的调用阻塞,直到线程完成执行.无论如何,你将不得不等待所有的线程.如果`t1`首先完成,你将开始等待`t2`(可能已经完成,你将立即继续等待`t3`).如果`t1`执行的时间最长,当你从它返回时,`t1`和`t2`将立即返回而不会阻塞. (20认同)
  • 嗯 - 无法理解某些东西,不要先运行t1,等到它完成,然后转到t2..etc等?如何让这一切一下子发生?我不知道这会如何同时运行它们? (5认同)
  • 说 t1 花费的时间最长,但 t2 有一个例外。那会发生什么?你能捕捉到那个异常或检查 t2 是否完成了吗? (4认同)
  • 好吧,我明白了.现在我理解了,有点困惑但是我想我明白了,`join`将当前进程附加到线程并等待直到完成,如果t2在t1之前完成然后当t1完成时它将检查t2正在完成,看到它是,然后检查t3..etc..etc ..然后只有当所有的完成它将继续.真棒. (2认同)

Ada*_*tan 24

我更喜欢使用基于输入列表的列表推导:

inputs = [scriptA + argumentsA, scriptA + argumentsB, ...]
threads = [Thread(target=call_script, args=(i)) for i in inputs]
[t.start() for t in threads]
[t.join() for t in threads]
Run Code Online (Sandbox Code Playgroud)

  • Checked answer解释得很好,但这个更短,不需要丑陋的重复。只是一个很好的答案。:) (2认同)
  • @VinayakKaniyarakkal`for t in thread:t.start()`不是更好吗? (2认同)

Rob*_*rto 21

在Python3中,由于Python 3.2有一种新的方法可以达到相同的结果,我个人更喜欢传统的线程创建/启动/加入,包concurrent.futures:https://docs.python.org/3/library/concurrent.futures html的

使用ThreadPoolExecutor代码将是:

from concurrent.futures.thread import ThreadPoolExecutor
import time

def call_script(ordinal, arg):
    print('Thread', ordinal, 'argument:', arg)
    time.sleep(2)
    print('Thread', ordinal, 'Finished')

args = ['argumentsA', 'argumentsB', 'argumentsC']

with ThreadPoolExecutor(max_workers=2) as executor:
    ordinal = 1
    for arg in args:
        executor.submit(call_script, ordinal, arg)
        ordinal += 1
print('All tasks has been finished')
Run Code Online (Sandbox Code Playgroud)

前面代码的输出类似于:

Thread 1 argument: argumentsA
Thread 2 argument: argumentsB
Thread 1 Finished
Thread 2 Finished
Thread 3 argument: argumentsC
Thread 3 Finished
All tasks has been finished
Run Code Online (Sandbox Code Playgroud)

其中一个优点是您可以控制吞吐量设置最大并发工作者.

  • @PrimeByDesign你可以使用`concurrent.futures.wait`函数,你可以在这里看到一个[真实的例子](https://github.com/shaftoe/api-l3x-in/blob/b82e3a888ecff261bcae8ffd7c8a9846ba616391/lib/stacks/pagespeed/ lambdas/pagespeed_poller.py#L69)官方文档:https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.wait (3认同)
  • 正如您在示例中看到的,当所有任务完成时,执行 `with` 语句之后的代码。 (2认同)
  • @Pranalee,该代码有效,我已经更新了代码以添加输出行。在所有线程完成之前,您无法看到“所有任务...”,这就是在这种情况下“with”语句设计的工作方式。无论如何,您随时可以在 SO 中提出一个新问题并发布您的代码,以便我们可以帮助您找出您的案例中发生的情况。 (2认同)

小智 5

你可以有类类似下面从中你可以添加你想要并行激情执行,并开始执行,并等待所有作业完成的功能或console_scripts"N"数字..

from multiprocessing import Process

class ProcessParallel(object):
    """
    To Process the  functions parallely

    """    
    def __init__(self, *jobs):
        """
        """
        self.jobs = jobs
        self.processes = []

    def fork_processes(self):
        """
        Creates the process objects for given function deligates
        """
        for job in self.jobs:
            proc  = Process(target=job)
            self.processes.append(proc)

    def start_all(self):
        """
        Starts the functions process all together.
        """
        for proc in self.processes:
            proc.start()

    def join_all(self):
        """
        Waits untill all the functions executed.
        """
        for proc in self.processes:
            proc.join()


def two_sum(a=2, b=2):
    return a + b

def multiply(a=2, b=2):
    return a * b


#How to run:
if __name__ == '__main__':
    #note: two_sum, multiply can be replace with any python console scripts which
    #you wanted to run parallel..
    procs =  ProcessParallel(two_sum, multiply)
    #Add all the process in list
    procs.fork_processes()
    #starts  process execution 
    procs.start_all()
    #wait until all the process got executed
    procs.join_all()
Run Code Online (Sandbox Code Playgroud)