Почему скрипт, использующий потоки, иногда печатает дополнительные строки? - PullRequest
7 голосов
/ 07 октября 2011

Если 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 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 ведут себя как положено (без пустых строк).

Изменения

  • sys.stdout.flush() после того, как print не решит проблему:

    with lock:
        print(s)
        sys.stdout.flush()
    
  • еще более странно, чем обычный sys.stdout.write() не производит пустых строк с блокировкой:

    with lock:
        sys.stdout.write(s)
        sys.stdout.write('\n') #NOTE: no .flush()
    
  • print функция работает как положено (без пустых строк).

Для воспроизведения скачайте файлы и запустите:

$ tox

Ответы [ 2 ]

4 голосов
/ 07 октября 2011

Взгляните на этот поток stackoverflow: Как получить потокобезопасную печать в 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

Вы можете видеть, что нить 3 отображается как завершающая перед печатью символа 'c'. Это явно не может иметь место, поэтому я предполагаю, что печать на консоль не является поточно-ориентированной.

Это, однако, не объясняет, почему печать в sys.stderr, кажется, работает правильно.

0 голосов
/ 18 июня 2019

Потому что сначала напечатать запись в стандартный вывод, а затем завершить строку.Псевдокод для объяснения:

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

так, в многопоточности первая строка обоих потоков выполняется раньше второй, поэтому одновременно выводятся две строки новой строки.Но почему линии не совпадают?Я не знаю.Необходимо глубже проверить реализацию печати python.

...