2013-01-06 55 views
4

我已經和Python一起工作了一段時間,但在今天之前我從未真正做過任何併發。我偶然發現this blog post,並決定做一個類似(但簡單)例如:爲什麼我的Python程序的輸出看起來很奇怪?

import os 
import threading 
import Queue 

class Worker(threading.Thread): 
    def __init__(self, queue, num): 
     threading.Thread.__init__(self) 
     self.queue = queue 
     self.num = num 

    def run(self): 
     while True: 
      text = self.queue.get() 
      #print "{} :: {}".format(self.num, text) 
      print "%s :: %s" % (self.num, text) 
      self.queue.task_done() 

nonsense = ["BLUBTOR", "more nonsense", "cookies taste good", "what is?!"] 
queue = Queue.Queue() 

for i in xrange(4): 
    # Give the worker the queue and also its "number" 
    t = Worker(queue, i) 
    t.setDaemon(True) 
    t.start() 

for gibberish in nonsense: 
    queue.put(gibberish) 

queue.join() 

這似乎很好地工作,但似乎有一些問題,我想不通的打印。幾個測試運行:

[email protected]:~/code/pythonthreading$ python owntest.py 
0 :: BLUBTOR 
1 :: more nonsense 
3 :: cookies taste good 
2 :: what is?! 
[email protected]:~/code/pythonthreading$ python owntest.py 
0 :: BLUBTOR 
2 :: more nonsense 
3 :: cookies taste good0 :: what is?! 

[email protected]:~/code/pythonthreading$ python owntest.py 
2 :: BLUBTOR 
3 :: more nonsense1 :: cookies taste good 

2 :: what is?! 
[email protected]:~/code/pythonthreading$ 

爲什麼輸出的格式奇怪呢?

+1

您可以使用'sys.stdout.write(message)'代替:) –

回答

6

print不是原子的。

以下行:

 print "%s :: %s" % (self.num, text) 

會轉換成字節碼如下:

  24 LOAD_CONST    1 ('%s :: %s') 
     27 LOAD_FAST    0 (self) 
     30 LOAD_ATTR    3 (num) 
     33 LOAD_FAST    1 (text) 
     36 BUILD_TUPLE    2 
     39 BINARY_MODULO  
     40 PRINT_ITEM   
     41 PRINT_NEWLINE  

正如你可以看到,有兩個印有字節碼(PRINT_ITEMPRINT_NEWLINE)。如果線程在兩者之間被搶佔,你會看到你所看到的。

我與其他人同意,sys.stdout.write()是一種更爲安全的選擇,因爲這種使用情況:

  1. 它強迫你,你把它寫(與print你可以不小心使用print a, b, c,和結束之前整個字符串格式化三個獨立的寫道而不是一個);
  2. 它迴避了softspace和自動換行符的問題,這兩個問題都可以在程序的其他部分與print語句交互。
+0

這是非常有用的信息。使用換行手工修復問題:'print「{} :: {} \ n」.format(self.num,text),' – cgt

6

打印不是線程安全的。

雖然有些字符由一個線程複製到stdout流,另一個線程計劃是印刷連帶將字符複製到stdout流。

結果是,您的stdout不包含print調用的謹慎結果,但是來自不同線程的混合輸出混雜在一起。

解決方法是用sys.stdout.write()代替; 原子(線程安全)操作。確保你包含一個明確的\n換行符。

相關問題