Я нашел этот абзац в Документах, в которых говорится:
Бинарные буферизованные объекты (экземпляры
BufferedReader
,BufferedWriter
,BufferedRandom
иBufferedRWPair
) защищают свои внутренние структуры с помощью блокировки; поэтому безопасно называть их сразу из нескольких потоков.
Я не уверен, почему они должны "защищать" свои внутренние структуры, учитывая, что GIL находится в действии. Какая разница? Мне было все равно, пока я не узнал, что этот замок имеет некоторое значение, рассмотрим этот фрагмент кода:
from _thread import start_new_thread
import time
def start():
for i in range(10):
print("SPAM SPAM SPAM!")
for i in range(10):
start_new_thread(start, ())
time.sleep(0.0001)
print("main thread exited")
Вывод при запуске на Python 3.X:
...many SPAM...
SPAM SPAM SPAM!
SPAM SPAM SPAM!
SPAM SPAM SPAM!
main thread exited
SPAM SPAM SPAM!
SPAM SPAM SPAM!
SPAM SPAM SPAM!
SPAM SPAM SPAM!
SPAM SPAM SPAM!
Fatal Python error: could not acquire lock for
<_io.BufferedWritername='<stdout>'> at interpreter shutdown, possibly due to daemon threads
В Python 2.7 отсутствуют ошибки. Я не знаю, почему это произойдет, однако, я искал в bufferedio.c. Другой код, который ведет себя аналогично приведенному выше фрагменту, который был протестирован на Python 3.X, иногда я получил Fatal Python error
, а иногда я этого не делал. Любая потоковая функция с циклом плюс std[out][err].write
вызывает эту фатальную ошибку. Очень сложно определить характеристики этой ошибки и, насколько мне известно, в документации ничего не говорится об этом. Я не уверен, даже если это ошибка, надеюсь, нет.
Мое объяснение этого поведения выглядит следующим образом: * Я мог быть совершенно неправ:
Основной поток выходил, удерживая замок sys.stdout.buffer
. Однако это противоречит тому факту, что потоки прекращаются, когда основной поток выходит из системы, на которой я запускаю Python, Linux.
Я отправляю это как ответ, это просто невозможно сделать в разделе комментариев.
Это поведение не ограничивается write
, оно влияет на read
, а также flush
вызывает те объекты BufferedReader
, BufferedWriter
, BufferedRandom
и BufferedRWPair
.
1) В Linux и, возможно, в Windows тоже, когда основной поток завершается, его дочерние потоки прекращаются. Как это влияет на указанное поведение? Если основной поток смог выйти во время его среза времени, перед тем как переключиться на контекст с другим потоком, фатальная ошибка не возникает, поскольку все потоки прекращаются. Однако ничего не гарантирует, что основной поток выйдет, как только он начнется.
2) Фатальная ошибка происходит между процессом финализации (выключения) интерпретатора и вызовом read
, write
или flush
и, возможно, другими операциями над объектом Buffered*
. Процесс завершения получает блокировку этих объектов, любой write
, например, для объекта BufferedWriter
, приводит к Fatal Python error
.
os._exit
завершает интерпретатор без шагов завершения и, следовательно, интерпретатор не будет владеть блокировкой объекта, о котором мы говорим, это еще один пример:
from _thread import start_new_thread
import time, sys, os
def start(myId):
for i in range(10):
sys.stdout.buffer.write(b"SPAM\n")
for i in range(2):
start_new_thread(start, (i,))
x = print("main thread")
print(x)
#os._exit(0)
В вышеприведенном коде, если основной поток выходит, как только он запускается, что он, фатальная ошибка не возникает, и все порожденные потоки немедленно прекращаются (по крайней мере, в Linux), это зависит от платформы. Если вам не повезло, и другой поток начал играть на поле до выхода основных потоков, без вызова os._exit(0)
интерпретатор проходит свой обычный цикл завершения, чтобы получить блокировку sys.stdout.buffer
, что приводит к фатальной ошибке. Запустите этот код несколько раз, чтобы заметить его разные поведения.