为什么我的Python程序的输出看起来很奇怪?

时间:2013-01-06 10:20:12

标签: python multithreading output

我已经使用Python一段时间了,但是在今天之前我从来没有真正做过任何并发。我偶然发现this blog post并决定制作一个类似的(但更简单的)例子:

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()

它似乎工作正常,但似乎有一些我无法弄清楚的打印问题。几个测试运行:

chris@DPC3:~/code/pythonthreading$ python owntest.py 
0 :: BLUBTOR
 1 :: more nonsense
3 :: cookies taste good
 2 :: what is?!
chris@DPC3:~/code/pythonthreading$ python owntest.py 
0 :: BLUBTOR
2 :: more nonsense
3 :: cookies taste good0 :: what is?!

chris@DPC3:~/code/pythonthreading$ python owntest.py 
2 :: BLUBTOR
 3 :: more nonsense1 :: cookies taste good

 2 :: what is?!
chris@DPC3:~/code/pythonthreading$

为什么输出格式奇怪?

2 个答案:

答案 0 :(得分:6)

打印不是线程安全的。

当一些线程将某些字符复制到stdout流时,另一个线程被 打印,并且它会将字符复制到stdout流。

结果是,您的stdout不包含print次调用的谨慎结果,而是来自不同线程的混合输出,所有这些都混杂在一起。

解决方法是使用sys.stdout.write()代替; 原子(线程安全)操作。确保包含明确的\n换行符。

答案 1 :(得分: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. 它回避了软件空间和自动换行的问题,这两个问题都可以与程序其他部分的print语句进行交互。