2017-06-20 83 views
3

我跑這個代碼爲什麼這10個線程總是輸出相同的線程名稱?

NUM = 0 
def count(): 
    global NUM 
    NUM += 1 
    time.sleep(1) 
    print(t.getName()+":"+"NUM is "+str(NUM)) 

for i in range(10): 
    t = threading.Thread(target=count) 
    t.start() 

輸出是

Thread-10:NUM is 10 
Thread-10:NUM is 10 
Thread-10:NUM is 10 
Thread-10:NUM is 10 
Thread-10:NUM is 10 
Thread-10:NUM is 10 
Thread-10:NUM is 10 
Thread-10:NUM is 10 
Thread-10:NUM is 10 
Thread-10:NUM is 10 

我知道爲什麼NUM始終是10,但爲什麼是線程的名字總是相同的?每個線程運行print(t.getName()+":"+"NUM is "+str(NUM)); t不應該是獲得CPU時間的線程?我認爲這個名字不應該是一樣的。

當我改變了這個

NUM = 0 
def count(): 
    global NUM 
    NUM += 1 
    name = t.getName() 
    time.sleep(1) 
    print(name+":"+"NUM is "+str(NUM)) 

for i in range(10): 
    t = threading.Thread(target=count) 
    t.start() 

它的工作原理如我所料:

Thread-1:NUM is 10 
Thread-3:NUM is 10 
Thread-2:NUM is 10 
Thread-4:NUM is 10 
Thread-5:NUM is 10 
Thread-7:NUM is 10 
Thread-10:NUM is 10 
Thread-9:NUM is 10 
Thread-6:NUM is 10 
Thread-8:NUM is 10 
+2

「t」在哪裏設置? –

+0

't'是一個*外*變量。例如重複設置直到達到10。 –

回答

6

這是因爲你參考全球名稱t。睡眠結束時,循環結束,並且t保持綁定到創建的循環的最後一個線程(第10個線程)。

在您的替代方案中,結果並未實際定義。有參考全球t循環仍在運行,所以它很可能會被綁定到最近創建的線程 - 但不是是。

注意:如果沒有便捷的線程對象正在運行,你可以使用

threading.currentThread() 

得到它。然後

threading.currentThread().getName() 

將返回運行它的線程的名稱。

+0

我試過你的代碼,它實際上工作。但爲什麼當使用'threading.currentThread()'工作時,文檔說>返回當前的Thread對象,對應於調用者的控制線程。 't'已經被設置爲for循環中的第10個線程,'currentThread()'不應該是相同的第10個線程? @Tim Peters – lxacoder

+1

「調用者」是調用'currentThread()'的線程,所以_always_是你想要的線程;-)這與線程的創建無關(特別是,與當時全球的't'碰巧無關)。 –

+1

@lxacoder可以這麼想:假設在主線程可以改變它的值之前,對'count'的每次調用運行的時間至少等於對't' *的引用。但爲什麼呢?唯一可以保證的是每個線程都是*創建的*您無法對每個線程在被再次掛起之前執行多少時間做出任何假設。 – chepner

4

你的t功能查詢,但沒有t在功能定義:

def count(): 
    global NUM 
    NUM += 1 
    name = t.getName() # use outer t 
    time.sleep(1) 
    print(name+":"+"NUM is "+str(NUM))

回退機制的Python因此將在外部直接範圍內尋找t。實際上,您在外部範圍內分配了t,因此它將採用該值。

現在,因爲你寫的for循環t = ...,即t迅速變化。在第一個線程實際獲取t之前,很有可能for循環已經達到最後一個值 - 特別是因爲Python的線程機制。因此,所有線程都會參考上一個構造的線程獲取t

如果我們不過重寫功能

NUM = 0 
def count(): 
    name = t.getName() # t fetched immediately 
    global NUM 
    NUM += 1 
    time.sleep(1) 
    print(name+":"+"NUM is "+str(NUM)) 

我得到:

Thread-11:NUM is 10 
Thread-12:NUM is 10 
Thread-13:NUM is 10 
Thread-14:NUM is 10 
Thread-15:NUM is 10 
Thread-17:NUM is 10 
Thread-16:NUM is 10 
Thread-19:NUM is 10 
Thread-18:NUM is 10 
Thread-10:NUM is 10 

我的機器上。當然這是不是保證每個線程都會抓取正確的線程,因爲有可能只有在進程後面,線程纔會開始工作並取回t變量。

+1

我想我知道這就是爲什麼在你的幫助之後。謝謝。 – lxacoder

+0

這肯定會減少當前線程在主線程改變之前失去訪問't'的可能性,但它並不能消除它。 – chepner

+0

@chepner:是的我知道,我只是想證明在這裏我們抓住了不同的't'值。但我在答案的底部添加了一個註釋。 –

2

對於線程名稱和計數NUM,您都有同樣的問題:到達第一個print語句時,所有10個線程都已啓動。你只有一個全局變量t爲線程,而一個爲全局爲NUM爲計數。因此,你看到的只是第10個線程的最後一個值。如果你想單獨打印數據,你需要爲你的代碼提供一個機制來在啓動時報告它們,或者保存一個你可以迭代的列表。

1

我建議你試試這個:

NUM = 0 
def count(): 
    global NUM 
    NUM += 1 
    num = NUM 
    name = t.getName() 
    time.sleep(1) 
    print("t.getName: " + t.getName() + ", name: " + name + ":" + ", NUM: " + str(NUM) + ", num: " + str(num)) 

for i in range(10): 
    t = threading.Thread(target=count) 
    t.start() 

結果:

t.getName: Thread-10, name: Thread-10:, NUM: 10, num: 10 
t.getName: Thread-10, name: Thread-6:, NUM: 10, num: 6 
t.getName: Thread-10, name: Thread-3:, NUM: 10, num: 3 
t.getName: Thread-10, name: Thread-5:, NUM: 10, num: 5 
t.getName: Thread-10, name: Thread-4:, NUM: 10, num: 4 
t.getName: Thread-10, name: Thread-9:, NUM: 10, num: 9 
t.getName: Thread-10, name: Thread-7:, NUM: 10, num: 7 
t.getName: Thread-10, name: Thread-2:, NUM: 10, num: 2 
t.getName: Thread-10, name: Thread-1:, NUM: 10, num: 1 
t.getName: Thread-10, name: Thread-8:, NUM: 10, num: 8 

t.getName()是一個函數調用,這是一個參考。當打印功能到達控制檯時,t引用最新的線程。

+0

與任何全局變量一樣,'t'受制於競爭條件,並且當任何特定線程的函數實際運行時,您無法確定哪個線程會引用't'。 – chepner