【问题标题】:end='...' key in print() not thread safe?end='...' print() 中的键不是线程安全的?
【发布时间】:2018-05-27 11:24:35
【问题描述】:

通过下面的代码,我尝试使用ThreadPoolExecutor 在 jupyter 笔记本上并行打印一堆东西。请注意,使用函数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 notebook 和编写 .py 文件并运行它都试过了。但是对于后者,我似乎没有遇到这个问题。我尝试搜索,但我得到的只是 python-3.x 中的print() 线程安全的。如果它确实是线程安全的,谁能解释为什么会这样?

【问题讨论】:

  • 当您执行print("%s "%(name), end ='') 时会发生什么? end= 变体可能会将名称和空格作为 distinct 操作输出,其中可能会在它们之间发生上下文切换。带有空格的单个字符串的print(以及一个空的end)可以缓解这种情况。
  • 如果你使用的是 Python 3.3+,你可能想试试print(..., flush=True')

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


【解决方案1】:

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

A
B
C
D
EF
G

H
I

即使flush=True 也无法修复它。

print函数是用CPythonhere实现的,用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 函数本身在做什么,这也解释了为什么做 print(name + " ", end="") 也有效。

【讨论】:

  • 感谢您的回答 :) 只是一个快速的后续问题:打印功能是否不是线程安全的?
  • 老实说,我不知道;也许this question的答案有帮助? This answer 似乎暗示,一般来说,文件 I/O 不是线程安全的,这意味着 print 也不是线程安全的。
猜你喜欢
  • 2011-02-10
  • 2010-10-15
  • 2012-05-11
  • 1970-01-01
  • 1970-01-01
  • 2017-10-24
相关资源
最近更新 更多