在print()中的end ='...'键不是线程安全的吗?

时间:2018-05-27 11:24:36

标签: python python-3.x jupyter-notebook jupyter

使用下面的代码,我尝试使用ThreadPoolExecutor在jupyter-notebook上并行打印一堆内容。请注意,使用函数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 线程安全。如果它确实是线程安全的,那么有人可以解释为什么会发生这种情况吗?

1 个答案:

答案 0 :(得分:3)

指定end实际上并不需要公开此内容;即使只是做print(name),有时会导致字母彼此相邻:

A
B
C
D
EF
G

H
I

即使flush=True也无法解决问题。

print函数在CPython here中实现,用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="")也有效。