2012-07-23 59 views
3

python新手,試圖理解多線程。下面是一個來自python文檔的例子,關於Queue基本python多線程問題

對於我生活中的這些,我不明白這個例子是如何工作的。在worker()函數中,有一個無限循環。工人如何知道什麼時候離開循環?似乎沒有破裂的情況。

到底什麼是聯結?我不應該加入線程嗎?

def worker(): 
    while True: 
     item = q.get() 
     do_work(item) 
     q.task_done() 

q = Queue() 
for i in range(num_worker_threads): 
    t = Thread(target=worker) 
    t.daemon = True 
    t.start() 

for item in source(): 
    q.put(item) 

q.join()  # block until all tasks are done 

另外一個問題,什麼時候應該使用多線程,何時應該使用多處理?

回答

6

是。你是對的。 worker將永遠運行。但是由於隊列只有有限數量的項目,最終worker將永久阻止在q.get()(因爲隊列中不會有更多項目)。在這一點上,worker仍在運行是無關緊要的。 q.join()阻塞,直到隊列計數下降到0(無論工作者線程調用q.task_done,計數下降1)。之後,程序結束。而無限阻塞的線程與它的創造者一起死亡。

+0

啊我現在明白了。有什麼網站/資源可以閱讀這些微妙之處嗎?文件真的不是它看起來最好的地方。 – JZee 2012-07-23 21:19:17

+1

除了主線程的末尾殺死子線程之外,所有這些信息都直接從您鏈接自己的'Queue'文檔頁面。 – 2012-07-23 21:20:16

+0

感謝您的回答。我想我會接受它,因爲它回答了我的主要問題。 – JZee 2012-07-24 17:24:45

5

關於第二個問題,Python中線程和進程之間最大的區別是主流實現使用全局解釋器鎖(GIL)來確保多個線程不能混淆Python的內部數據結構。這意味着對於花費大部分時間在純Python中進行計算的程序,即使使用多個CPU,也不會加快程序的運行速度,因爲一次只有一個線程可以保存GIL。另一方面,多線程可以輕鬆地在Python程序中共享數據,而在一些情況下(但絕非全部),您不必過多擔心線程安全問題。

多線程可以加快Python程序的運行速度,當程序花費大部分時間等待I/O - 磁盤訪問或尤其是網絡操作時。在執行I/O操作時,GIL不被佔用,因此很多Python線程可以在I/O綁定的應用程序中同時運行。另一方面,通過多處理,每個進程都有自己的GIL,因此您的性能可以擴展到可用的CPU核心數量。不利的一面是進程之間的所有通信都必須通過multiprocessing.Queue完成(它在表面上非常像Queue.Queue,但由於它必須跨進程邊界進行通信,因此具有非常不同的底層機制)。

因爲通過線程安全或進程間隊列工作避免了很多潛在的線程問題,並且由於Python使得它非常容易,所以multiprocessing模塊非常有吸引力。

+0

很好的回答,這是一個恥辱,因爲我剛剛註冊,所以無法投票。我正在做多線程/處理,因爲我需要並行讀取多個數據文件(數以千計),從中讀取一些信息,然後將文件寫回到磁盤上(再次以千爲單位)。那麼我應該使用多線程還是多處理?似乎我會做相當數量的I/O。 – JZee 2012-07-23 21:39:25

+0

而且我也不清楚如何「加入」這些過程。我在最後有一個循環,循環遍歷每個創建的進程,並調用join,但是我的代碼仍然沒有終止。 – JZee 2012-07-23 22:14:05

+0

如果主線程調用'childThread.join()',則子線程必須自行終止,以上示例代碼不會執行此操作。你的示例代碼使用'Queue.join()',這是非常不同的。現在可能是採取現有代碼並提出新問題的時候了。 – 2012-07-23 22:53:01

0

同意joel-cornett,主要是。我試圖在python2.7運行下面的代碼片段:

from threading import Thread 
from Queue import Queue 

def worker(): 
    def do_work(item): 
     print(item) 

    while True: 
     item = q.get() 
     do_work(item) 
     q.task_done() 

q = Queue() 
for i in range(4): 
    t = Thread(target=worker) 
    t.daemon = True 
    t.start() 

for item in range(10): 
    q.put(item) 

q.join() 

輸出是:

0 
1 
2 
3 
4 
5 
6 
7 
8 
9 
Exception in thread Thread-3 (most likely raised during interpreter shutdown): 
Traceback (most recent call last): 
    File "/usr/lib/python2.7/threading.py", line 551, in __bootstrap_inner 
    File "/usr/lib/python2.7/threading.py", line 504, in run 
    File "abc.py", line 9, in worker 
    File "/usr/lib/python2.7/Queue.py", line 168, in get 
    File "/usr/lib/python2.7/threading.py", line 236, in wait 
<type 'exceptions.TypeError'>: 'NoneType' object is not callable 

最可能的解釋,我認爲:

隨着隊列任務用盡後再變空,父線程從q.join()返回並退出隊列後退出。子線程在接收到「item = q.get()」中產生的第一個TypeError異常時終止,因爲隊列不再存在。

+0

如果我刪除'q.join()'並確保父線程在處理完成之前死亡,我的結果與您相同。 – 2012-07-23 21:43:59