为什么使用线程的脚本偶尔会打印额外的行?

时间:2011-10-07 13:21:39

标签: python multithreading io buffering

如果print sprint >>sys.stderr, s替换,则效果会消失。

import random, sys, time
import threading

lock = threading.Lock()

def echo(s):
    time.sleep(1e-3*random.random()) # instead of threading.Timer()
    with lock:
        print s

for c in 'abc':
    threading.Thread(target=echo, args=(c,)).start()

实施例

# Run until empty line is found:
$ while ! python example.py 2>&1|tee out|grep '^$';do echo -n .;done;cat out

输出

....................
b

c
a

输出不应包含空行,但确实如此。我知道print不是线程安全的,但我认为锁应该有用。

问题是为什么会发生这种情况?

我的机器:

$ python -mplatform
Linux-2.6.38-11-generic-x86_64-with-Ubuntu-11.04-natty

在py26,py27,pypy上打印额外的行。

py24,py25,py31,py32表现得如预期的那样(没有空行)。

变体形式

    print无法解决问题后
  • sys.stdout.flush()

    with lock:
        print(s)
        sys.stdout.flush()
    
  • 更奇怪的是,普通sys.stdout.write()不会产生带锁的空行:

    with lock:
        sys.stdout.write(s)
        sys.stdout.write('\n') #NOTE: no .flush()
    
  • print function按预期工作(没有空行)。

要重现download files并运行:

$ tox

2 个答案:

答案 0 :(得分:4)

看看这个stackoverflow线程:How do I get a thread safe print in Python 2.6?。显然,打印到sout不是线程安全的。

如果打开详细线程,您可以更好地看到它:

threading.Thread(target=echo, args=(c,), verbose=True).start()

我得到这样的输出:

MainThread: <Thread(Thread-1, initial)>.start(): starting thread
Thread-1: <Thread(Thread-1, started 6204)>.__bootstrap(): thread started
MainThread: <Thread(Thread-2, initial)>.start(): starting thread
Thread-2: <Thread(Thread-2, started 3752)>.__bootstrap(): thread started
MainThread: <Thread(Thread-3, initial)>.start(): starting thread
Thread-3: <Thread(Thread-3, started 4412)>.__bootstrap(): thread started
MainThread: <Thread(Thread-2, started 3752)>.join(): waiting until thread stops
a
b
Thread-1: <Thread(Thread-1, started 6204)>.__bootstrap(): normal return
Thread-2: <Thread(Thread-2, started 3752)>.__bootstrap(): normal return
MainThread: <Thread(Thread-2, stopped 3752)>.join(): thread stopped
MainThread: <Thread(Thread-3, started 4412)>.join(): waiting until thread stops
Thread-3: <Thread(Thread-3, started 4412)>.__bootstrap(): normal return
MainThread: <Thread(Thread-3, stopped 4412)>.join(): thread stopped
c

在打印'c'字符之前,您可以看到线程3显示为完成。显然情况并非如此,因此这使我假设打印到控制台不是线程安全的。

然而,这并不能解释为什么打印到sys.stderr似乎能正常工作。

答案 1 :(得分:0)

因为先打印先写入标准输出文本,然后再写入结束字符串。伪代码解释:

def print(*args, **kwargs):
    write_to_stdout(to_single_string(args))
    write_to_stdout(end)  # usually a newline "\n"

因此,在多线程处理中,两个线程的第一个字符串在第二个字符串之前执行,因此同时打印了两个换行符。但是,为什么行不在同一行上?我不知道。需要更深入地检查python打印的实现。