何时,为什么以及如何在Python中调用thread.join()?

时间:2014-01-31 17:16:35

标签: python multithreading

我有这个python线程代码。

import threading

def sum(value):
    sum = 0
    for i in range(value+1):
        sum += i
    print "I'm done with %d - %d\n" % (value, sum)
    return sum

r = range(500001, 500000*2, 100)

ts = []
for u in r:
    t = threading.Thread(target=sum, args = (u,))
    ts.append(t)
    t.start()

for t in ts:
   t.join()

执行此操作,我有数百个线程在工作。

enter image description here

但是,当我在t.start()之后移动t.join()时,我只有两个线程在工作。

for u in r:
    t = threading.Thread(target=sum, args = (u,))
    ts.append(t)
    t.start()
    t.join()

enter image description here

我使用调用t.join()的代码进行了测试,但似乎工作正常吗?

那么何时,如何以及如何使用thread.join()?

3 个答案:

答案 0 :(得分:4)

您似乎不明白Thread.join的作用。调用join时,当前线程将阻止,直到该线程结束。所以你正在等待线程完成,阻止你启动任何其他线程。

join背后的想法是在继续之前等待其他线程。在您的情况下,您希望等待所有线程在主程序结束时完成。否则,如果你没有这样做,并且主程序将结束,那么它创建的所有线程都将被终止。所以通常,你应该在最后有一个循环,它连接所有创建的线程,以防止主线程提前退出。

答案 1 :(得分:3)

简短回答:这一个:

for t in ts:
   t.join()

通常是启动线程数的惯用方法。执行.join意味着主线程在执行之前等待给定线程完成。您通常在启动所有线程后执行此操作。

更长的答案:

len(list(range(500001, 500000*2, 100)))
Out[1]: 5000

您尝试一次启动5000个主题。你的电脑仍然是一块奇迹!

您在调度工作程序的循环中.join的方法永远不会有超过2个线程(即只有一个工作线程)同时进行。您的主线程必须等待每个工作线程完成才能继续下一个。您已经防止了计算机崩溃,但是您的代码将比您从未首先使用过线程更慢!

此时我还在谈论GIL,但我暂时把它放在一边。将线程创建限制为合理限制(即多于一个,小于5000)所需的是ThreadPool。有多种方法可以做到这一点。你可以自己动手 - 这对于threading.Semaphore来说相当简单。您可以使用3.2 +' concurrent.futures包。你可以使用一些第三方解决方案。由您决定,每个API都会有不同的API,因此我无法进一步讨论。


强制性GIL讨论

cPython程序员必须与GIL一起生活。简而言之,Global Interpreter Lock意味着只有一个线程可以一次执行python字节码。这意味着在处理器绑定的任务上(比如添加一堆数字),线程不会导致任何加速。实际上,设置和拆除线程所涉及的开销(更不用说上下文切换)将导致速度减慢。线程处理更有利于提高I / O绑定任务的收益,例如检索一堆URL。

multiprocessing和朋友们通过使用多个进程来回避GIL限制。这不是免费的 - 进程之间的数据传输是昂贵的,因此需要非常谨慎地不要编写依赖于共享状态的工作者。

答案 2 :(得分:1)

join()等待你的线程完成,所以第一次使用会启动一百个线程,然后等待所有线程完成。第二次使用在每个线程启动另一个线程之前等待它的结束,这样做会破坏线程的目的。

第一次使用最有意义。你运行线程(所有这些)进行一些并行计算,然后等到所有这些都完成后再继续使用结果,以确保工作完成(即结果实际存在)。 / p>