2013-02-26 25 views
89

我正在研究python線程並遇到了join()在python線程中使用join()有什麼用途

作者告訴我,如果線程處於守護進程模式,那麼我需要使用join(),這樣線程才能在主線程終止之前自行完成。

,但我一直在使用t.join()即使t沒有daemon

示例代碼也沒見他是這個

import threading 
import time 
import logging 

logging.basicConfig(level=logging.DEBUG, 
        format='(%(threadName)-10s) %(message)s', 
        ) 

def daemon(): 
    logging.debug('Starting') 
    time.sleep(2) 
    logging.debug('Exiting') 

d = threading.Thread(name='daemon', target=daemon) 
d.setDaemon(True) 

def non_daemon(): 
    logging.debug('Starting') 
    logging.debug('Exiting') 

t = threading.Thread(name='non-daemon', target=non_daemon) 

d.start() 
t.start() 

d.join() 
t.join() 

我不知道什麼是使用t.join()因爲它不是守護進程我可以看到沒有變化,即使我刪除它

+6

標題+1。 '加入'似乎是專門用來鼓勵糟糕的性能,(通過不斷創建/終止/銷燬線程),GUI鎖定,(等待事件處理程序)和應用程序關閉失敗(等待不可中斷的線程終止)。注意 - 不僅僅是Python,這是一種跨語言的反模式。 – 2013-02-26 11:00:50

回答

164

一個有點笨拙的ascii藝術來演示的機制: join()大概是由主線程調用。它也可以被另一個線程調用,但是會不必要地使圖變複雜。

join -alling應放置在主線程的軌道上,但爲了表達線程關係並儘可能保持簡單,我選擇將它放在子線程中。

without join: 
+---+---+------------------      main-thread 
    | | 
    | +...........       child-thread(short) 
    +..................................   child-thread(long) 

with join 
+---+---+------------------***********+###  main-thread 
    | |        | 
    | +...........join()   |   child-thread(short) 
    +......................join()......   child-thread(long) 

with join and demon thread 
+-+--+---+------------------***********+###  parent-thread 
    | | |        | 
    | | +...........join()   |  child-thread(short) 
    | +......................join()......  child-thread(long) 
    +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,  child-thread(long+demonized) 

'-' main-thread/parent-thread/main-program execution 
'.' child-thread execution 
'#' optional parent-thread execution after join()-blocked parent-thread could 
    continue 
'*' main-thread 'sleeping' in join-method, waiting for child-thread to finish 
',' demonized thread - 'ignores' lifetime of other threads; 
    terminates when main-programs exits; is normally meant for 
    join-independent tasks 

所以你看不到任何改動的原因是因爲你的主線程您join後什麼都不做。 您可以說join(僅)與主線程的執行流程相關。

例如,如果您想同時下載一堆頁面以將它們連接成一個大頁面,則可以使用線程啓動併發下載,但需要等到最後一頁/線程完成後再啓動彙集了許多單頁。那時你使用join()

+0

請確認守護程序線程可以加入()而不阻止程序執行? – Aviator45003 2015-03-03 08:52:30

+0

@Aviator45003:是的,通過使用超時參數,例如:'demon_thread.join(0.0)','join()'在默認情況下阻塞,而不考慮daemonized屬性。但加入惡魔化線程很可能會造成一連串麻煩!我現在正考慮在守護進程線程的小圖中刪除join()調用...... – 2015-03-03 17:15:27

10

方法連接()

會阻塞調用線程,直到調用join()方法的線程終止。

來源:http://docs.python.org/2/library/threading.html

+5

那麼加入的用途是什麼?看到OP的問題,不要只是解釋文檔 – 2013-02-26 09:49:18

+0

@DonQuestion我甚至嘗試在非守護線程中添加sleep.timer(20)而不使用't.join()',並且程序在終止之前仍然等待它。在我的代碼 – user192362127 2013-02-26 09:54:03

+0

中,我看不到任何't.join()'的使用,請參閱我的回答,以獲得進一步解釋。關於你的sleep.timer在非惡魔 - >惡魔線程是它的父線程的生命週期的解耦,所以父/兄弟線程不會受到被惡魔化線程的生命週期的影響,反之亦然。 – 2013-02-26 10:10:30

38

直從docs

加入([超時]) 等待,直到線程終止。這會阻塞調用線程,直到調用join()方法的線程終止 - 通常或通過未處理的異常 - 或直到發生可選的超時。

這意味着,滋生td主線程,等待t完成直到它完成。

根據您的程序使用的邏輯,您可能需要等到某個線程完成後再繼續執行主線程。

從文檔

另外:

一個線程可以被標記爲「守護線程」。這個標誌的意義在於,只有守護進程線程退出時,整個Python程序纔會退出。

一個簡單的例子,假設我們有這樣的:

def non_daemon(): 
    time.sleep(5) 
    print 'Test non-daemon' 

t = threading.Thread(name='non-daemon', target=non_daemon) 

t.start() 

與完成:

print 'Test one' 
t.join() 
print 'Test two' 

這將輸出:

Test one 
Test non-daemon 
Test two 

這裏的主線程明確等待爲t線程完成,直到我t第二次撥打print

或者,如果我們有這樣的:

print 'Test one' 
print 'Test two' 
t.join() 

我們會得到這樣的輸出:

Test one 
Test two 
Test non-daemon 

在這裏,我們做我們的工作在主線程,然後我們等待t線程完成。在這種情況下,我們甚至可能會刪除顯式加入t.join(),程序將隱式等待t完成。

+0

你可以對我的代碼進行一些修改,以便我可以看到't.join()'的區別。通過增加soome睡眠或其他東西。目前,即使我使用它,我也可以在程序中看到任何chnage。但對於damemon,我可以看到它的退出,如果我使用'd.join()',我沒有看到當我不使用d.join() – user192362127 2013-02-26 09:46:19

+0

@ user192362127看看更新的答案。 – dmg 2013-02-26 10:01:52

0

當爲非守護進程線程和守護進程線程建立連接(t)函數時,主線程(或主進程)應該等待t秒,然後才能進一步處理它自己的進程。在t秒的等待時間內,兩個子線程都應該做他們可以做的事情,比如打印一些文本。在t秒之後,如果非守護線程仍然沒有完成它的工作,並且在主進程完成它的工作後它仍然可以完成它,但是對於守護進程線程,它只是錯過了它的機會性窗口。但是它會在python程序退出後最終死掉。如果有什麼問題,請糾正我。

-3

「使用join()有什麼用?」你說。真的,它與「關閉文件有什麼關係,因爲當我的程序退出時,python和操作系統會爲我關閉文件」。

這只是一個好的編程問題。您應該在代碼中加入()您的線程,線程應該而不是正在運行,要麼是因爲您肯定必須確保線程沒有運行來干擾您自己的代碼,或者您想要正確行爲在一個更大的系統中。

僅僅因爲join()需要額外的時間,你可能會說「我不想讓我的代碼延遲給出答案」。在某些情況下,這可能是完全有效的,但是現在需要考慮到您的代碼「正在爲python和操作系統清理乾淨」。如果您出於性能原因這樣做,我強烈建議您記錄該行爲。如果您正在構建其他人需要使用的庫/軟件包,則尤其如此。

沒有理由不加入(),除了性能方面的原因,我認爲你的代碼不需要執行那麼好。

+4

關於清理線程的說法並非如此。看看threading.Thread.join()的源代碼。該函數所做的就是等待一個鎖,然後返回。沒有任何東西真的被清理。 – Collin 2015-07-16 14:01:01

+1

@Collin - 線程本身可能會佔用資源,在這種情況下,解釋器和操作系統確實需要清理「cruft」。 – qneill 2015-12-01 05:27:10

+1

再一次看看threading.Thread.join()的源代碼。那裏沒有任何東西可以觸發資源的收集。 – Collin 2015-12-01 16:01:29

13

感謝這個主題 - 它也幫了我很多。

今天我學到了一些關於.join()的東西。

這些線程並行運行:

d.start() 
t.start() 
d.join() 
t.join() 

這些順序運行(不是我想要的):

d.start() 
d.join() 
t.start() 
t.join() 

特別,我想聰明,整潔:

class Kiki(threading.Thread): 
    def __init__(self, time): 
     super(Kiki, self).__init__() 
     self.time = time 
     self.start() 
     self.join() 

This Works!但它按順序運行。我可以將self.start()放入__ init __中,但不要放在self.join()中。這必須在之後之後每個線程都已啓動。

join()是什麼導致主線程等待你的線程完成。否則,你的線程自行運行。

所以,一種將join()看作主線程中的「hold」的方法 - 它可以在主線程繼續之前對主線程中的線程進行取消線程並按順序執行。它確保您的線程在主線程向前移動之前完成。請注意,這意味着在調用join()之前,如果線程已經完成,那麼就可以了 - 當調用join()時,主線程會立即釋放。

事實上,它剛剛發生在我之前,主線程在d.join()等待,直到線程d完成它移動到t.join()之前。

其實是很清楚,考慮下面的代碼:(注意打印報表是如何旋入對方)

import threading 
import time 

class Kiki(threading.Thread): 
    def __init__(self, time): 
     super(Kiki, self).__init__() 
     self.time = time 
     self.start() 

    def run(self): 
     print self.time, " seconds start!" 
     for i in range(0,self.time): 
      time.sleep(1) 
      print "1 sec of ", self.time 
     print self.time, " seconds finished!" 


t1 = Kiki(3) 
t2 = Kiki(2) 
t3 = Kiki(1) 
t1.join() 
print "t1.join() finished" 
t2.join() 
print "t2.join() finished" 
t3.join() 
print "t3.join() finished" 

它產生這樣的輸出

$ python test_thread.py 
32 seconds start! seconds start!1 

seconds start! 
1 sec of 1 
1 sec of 1 seconds finished! 
21 sec of 
3 
1 sec of 3 
1 sec of 2 
2 seconds finished! 
1 sec of 3 
3 seconds finished! 
t1.join() finished 
t2.join() finished 
t3.join() finished 
$ 

的T1。 join()支持主線程。所有三個線程在t1.join()完成之前完成並且主線程繼續執行打印,然後t2.join()然後打印,然後t3.join()打印。

更正歡迎。我也是線程新手。

(注:如果你有興趣,我在寫代碼的DrinkBot,我需要線程運行成分同時泵而不是按順序 - 更少的時間來等待每次喝。)

+0

嘿,我也是新的python線程和主線程混淆,是第一個線程是主線程,如果不是,請指導我? – 2016-08-11 06:14:57

+0

主線程是程序本身。每個線程都從那裏分叉。然後它們被連接回來 - 因爲在join()命令中,程序一直等到線程完成後才繼續執行。 – 2017-01-03 05:16:07

相關問題