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

jfs*_*jfs 7 python io multithreading buffering

如果print s被替换,print >>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 Code Online (Sandbox Code Playgroud)

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

产量

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

c
a
Run Code Online (Sandbox Code Playgroud)

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

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

我的机器:

$ python -mplatform
Linux-2.6.38-11-generic-x86_64-with-Ubuntu-11.04-natty
Run Code Online (Sandbox Code Playgroud)

额外的线条印在py26,py27,pypy上.

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

变化

要重现下载文件并运行:

$ tox
Run Code Online (Sandbox Code Playgroud)

小智 4

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

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

threading.Thread(target=echo, args=(c,), verbose=True).start()
Run Code Online (Sandbox Code Playgroud)

我得到这样的输出:

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
Run Code Online (Sandbox Code Playgroud)

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

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