【问题标题】:How can I do an atomic write to stdout in python?如何在 python 中对标准输出进行原子写入?
【发布时间】:2014-09-18 17:06:56
【问题描述】:

我在一些资料中读到 print 命令不是线程安全的,解决方法是改用 sys.stdout.write 命令,但仍然如此对我不起作用,写入 STDOUT 不是原子的。

这是一个简短的示例(称为此文件parallelExperiment.py):

   import os
   import sys
   from multiprocessing import Pool

   def output(msg):
    msg = '%s%s' % (msg, os.linesep)
    sys.stdout.write(msg)

   def func(input):
    output(u'pid:%d got input \"%s\"' % (os.getpid(), str(input)))

   def executeFunctionInParallel(funcName, inputsList, maxParallelism):
       output(u'Executing function %s on input of size %d with maximum parallelism of %d' % (
           funcName.__name__, len(inputsList), maxParallelism))
       parallelismPool = Pool(processes=maxParallelism)
       executeBooleanResultsList = parallelismPool.map(funcName, inputsList)
       parallelismPool.close()
       output(u'Function %s executed on input of size %d  with maximum parallelism of %d' % (
           funcName.__name__, len(inputsList), maxParallelism))
       # if all parallel executions executed well - the boolean results list should all be True
       return all(executeBooleanResultsList)

   if __name__ == "__main__":
    inputsList=[str(i) for i in range(20)]
    executeFunctionInParallel(func, inputsList, 4)

查看输出:

我。调用 python parallelExperiment.py 的输出(注意“pid”这个词在某些行中是混乱的):

Executing function func on input of size 20 with maximum parallelism of 4
ppid:2240 got input "0"
id:4960 got input "2"
pid:4716 got input "4"
pid:4324 got input "6"
ppid:2240 got input "1"
id:4960 got input "3"
pid:4716 got input "5"
pid:4324 got input "7"
ppid:4960 got input "8"
id:2240 got input "10"
pid:4716 got input "12"
pid:4324 got input "14"
ppid:4960 got input "9"
id:2240 got input "11"
pid:4716 got input "13"
pid:4324 got input "15"
ppid:4960 got input "16"
id:2240 got input "18"
ppid:2240 got input "19"
id:4960 got input "17"
Function func executed on input of size 20  with maximum parallelism of 4

二。调用python parallelExperiment.py > parallelExperiment.log的输出,表示将stdout重定向到parallelExperiment.log文件(注意顺序行不好,因为在调用 executeFunctionInParallel 之前和之后并行调用 func,应该打印一条消息):

pid:3244 got input "4"
pid:3244 got input "5"
pid:3244 got input "12"
pid:3244 got input "13"
pid:240 got input "0"
pid:240 got input "1"
pid:240 got input "8"
pid:240 got input "9"
pid:240 got input "16"
pid:240 got input "17"
pid:1268 got input "2"
pid:1268 got input "3"
pid:1268 got input "10"
pid:1268 got input "11"
pid:1268 got input "18"
pid:1268 got input "19"
pid:3332 got input "6"
pid:3332 got input "7"
pid:3332 got input "14"
pid:3332 got input "15"
Executing function func on input of size 20 with maximum parallelism of 4
Function func executed on input of size 20  with maximum parallelism of 4

【问题讨论】:

  • 混乱的结果是多线程的预期结果。您生成的线程都有自己的时间线。 bash 弄乱字符串 pid 的事实并不少见。正如你所说,主要是它并不能很好地接受多种受体。它只是打印它得到的东西,有时会弄乱重叠的回调。

标签: python multiprocessing output stdout atomic


【解决方案1】:

这是因为multiprocessing.Pool 实际上使用子进程而不是线程。 您需要在进程之间使用显式synchronization。请注意,链接上的示例可以解决您的问题。

import os
import sys
from multiprocessing import Pool, Lock

lock = Lock()

def output(msg):
    msg = '%s%s' % (msg, os.linesep)
    with lock:
        sys.stdout.write(msg)

def func(input):
    output(u'pid:%d got input \"%s\"' % (os.getpid(), str(input)))

def executeFunctionInParallel(funcName, inputsList, maxParallelism):
    output(u'Executing function %s on input of size %d with maximum parallelism of %d' % (
      funcName.__name__, len(inputsList), maxParallelism))
    parallelismPool = Pool(processes=maxParallelism)
    executeBooleanResultsList = parallelismPool.map(funcName, inputsList)
    parallelismPool.close()
    parallelismPool.join()
    output(u'Function %s executed on input of size %d  with maximum parallelism of %d' % (
       funcName.__name__, len(inputsList), maxParallelism))
    # if all parallel executions executed well - the boolean results list should all be True
    return all(executeBooleanResultsList)

if __name__ == "__main__":
    inputsList=[str(i) for i in range(20)]
    executeFunctionInParallel(func, inputsList, 4)

【讨论】:

  • 我可以使用 Pool.map 方法吗?该示例用于使用“进程”对象
  • 当然可以。池本身在内部使用进程对象。
  • 很抱歉,我没有关注.. 我应该在 executeFunctionInParallelfunc 方法中进行哪些更改?
  • 我用你的代码的编辑版本更新了我的答案
  • 你不应该在close()之后做join()吗?
【解决方案2】:

如果您想避免锁定并乐于使用较低级别的接口,您可以使用os.openos.write 获得 POSIX O_APPEND 行为(如果您的系统支持);并查看Is file append atomic in UNIX?

【讨论】:

    猜你喜欢
    • 2012-01-18
    • 2015-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多