我已经使用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$
为什么输出格式奇怪?
答案 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_ITEM
和PRINT_NEWLINE
)。如果线程在两者之间被抢占,你会看到你所看到的。
我同意其他人认为sys.stdout.write()
对此用例更安全,因为:
print
,您可能会意外地使用print a, b, c,
并最终使用三次单独的写入而不是一次写入; print
语句进行交互。