С помощью приведенного ниже кода я попытался распечатать кучу вещей параллельно на ноутбуке jupyter, используя ThreadPoolExecutor. Обратите внимание, что с функцией show() вывод не такой, как вы обычно ожидаете.
from concurrent.futures import ThreadPoolExecutor
import sys
items = ['A','B','C','D','E','F',
'G','H','I','J','K','L',
'M','N','O','P','Q','R',
'S','T','U','V','W','X','Y','Z']
def show(name):
print(name, end=' ')
with ThreadPoolExecutor(10) as executor:
executor.map(show, items)
# This outputs
# AB C D E F G H I J KLMNOP QR STU VW XY Z
Но когда я пытаюсь использовать sys.stdout.write(), такого поведения не наблюдается.
def show2(name):
sys.stdout.write(name + ' ')
with ThreadPoolExecutor(10) as executor:
executor.map(show2, items)
# This gives
# A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
Странно то, что я пробовал это как на ноутбуке jupyter, так и написав файл .py и запустив его. Но с последним, похоже, у меня нет этой проблемы. Я попытался выполнить поиск, но все, что я получил, это print() в потокобезопасности python-3.x является. Если это действительно потокобезопасность, может ли кто-нибудь объяснить, почему это происходит?
Что происходит, когда вы делаете print("%s "%(name), end ='')? Возможно, вариант end= выводит имя и пространство как операции отчетливый, между которыми может происходить переключение контекста. print из одной строки с уже добавленным пробелом (и пустой end) может облегчить это.
Если вы используете Python 3.3+, вы можете попробовать print(..., flush=True').






Указание end на самом деле не требуется, чтобы раскрыть это; даже простое выполнение print(name) иногда приводит к тому, что буквы располагаются рядом друг с другом:
A
B
C
D
EF
G
H
I
Даже flush=True не исправляет.
Функция печати реализована в CPython здесь и написана на C. Интересный момент:
for (i = 0; i < nargs; i++) {
if (i > 0) {
if (sep == NULL)
err = PyFile_WriteString(" ", file);
else
err = PyFile_WriteObject(sep, file,
Py_PRINT_RAW);
if (err)
return NULL;
}
err = PyFile_WriteObject(args[i], file, Py_PRINT_RAW);
if (err)
return NULL;
}
if (end == NULL)
err = PyFile_WriteString("\n", file);
else
err = PyFile_WriteObject(end, file, Py_PRINT_RAW);
Вы можете видеть, что он вызывает PyFile_WriteObject один раз для каждого аргумента (и для sep, если он указан), а затем еще раз для аргумента end (PyFile_WriteString - это просто оболочка вокруг PyFile_WriteObject, которая принимает const char*, а не PyObject) - я предполагаю, что есть заканчивается возможностью переключения контекста где-то между этими вызовами.
Каждый вызов PyFile_WriteString по существу аналогичен вызову (в Python) sys.stdout.write, что объясняет, почему вы не видите этого при выполнении sys.stdout.write(name + ' '); если вы вместо этого сделали это:
sys.stdout.write(name)
sys.stdout.write(" ")
это больше похоже на то, что делает сама функция печати, что также объясняет, почему выполнение print(name + " ", end = "") тоже работает.
Спасибо за этот ответ :) Небольшой уточняющий вопрос: тогда функция печати не является поточно-ориентированной?
Честно говоря, не могу сказать; может ответы на этот вопрос помогут? Этот ответ, похоже, предполагает, что в целом файловый ввод-вывод не является потокобезопасным, что означает, что print также не является потокобезопасным.