OS 派生进程中的环境变量读取

OS environment variable reading in a spawned process

这类似于 a previous question,但 multiprocessing 而不是 subprocess。似乎动态更改 PYTHONHASHSEED 在使用 multiprocessing 时没有效果,不像 subprocess:

#check_environ.py
import os, multiprocessing, subprocess, sys

s = 'hello'
print('parent', os.getenv('PYTHONHASHSEED'), hash(s))

if len(sys.argv) > 1:
    os.environ['PYTHONHASHSEED'] = sys.argv[1]
subprocess.call(['python', '-c', "import os;print('subprocess', os.getenv('PYTHONHASHSEED'), hash('{}'))".format(s)])
multiprocessing.Process(target=lambda:print('multiprocessing', os.getenv('PYTHONHASHSEED'), hash(s))).start()

样本运行:

# explicit PYTHONHASHSEED for subprocess/multiprocessing 
$ python check_environ.py 12

parent None 4472558296122225349
subprocess 12 -8207222429063474615
multiprocessing 12 4472558296122225349

# random PYTHONHASHSEED for subprocess/multiprocessing 
$ python check_environ.py

parent None 7990499464460966677
subprocess None 1081030409066486350
multiprocessing None 7990499464460966677

所以无论如何,multiprocessing 哈希使用与父哈希相同的种子。有没有办法强制 multiprocessing 生成的子进程使用不同的哈希种子?

每个 python 进程都在 new OS 环境中启动,而 multiprocessing 只有 一个,共享,从父进程继承。

您可以使用 'fork' 以外的其他启动方法来创建进程。您的 OS 正在使用 fork(您不会因为使用 lambda 作为目标而获得 PicklingError)。

您可以使用 multiprocessing.set_start_method('spawn') 将启动方法更改为 'spawn'(Windows 上的默认且唯一选项)或更改为 'forkserver'(如果可用)。使用 multiprocessing.get_all_start_methods().

获取所有可用方法
#check_environ.py
import sys, os, subprocess
import multiprocessing as mp


def show(s):
    print('multiprocessing', os.getenv('PYTHONHASHSEED'), hash(s))


if __name__ == '__main__':

    mp.set_start_method('spawn')

    s = 'hello'
    print('parent', os.getenv('PYTHONHASHSEED'), hash(s))

    if len(sys.argv) > 1:
        os.environ['PYTHONHASHSEED'] = sys.argv[1]

    cmd = "import os; " \
          "print('subprocess', os.getenv('PYTHONHASHSEED'), hash('{}'))"
    subprocess.call(['python', '-c', cmd.format(s)])
    p = mp.Process(target=show, args=(s,))
    p.start()
    p.join()

终端输出:

$ python check_environ.py 12

parent None 4279361553958749032
subprocess 12 -8207222429063474615
multiprocessing 12 -8207222429063474615

如果您需要多次在启动方法之间切换,请使用上下文对象来设置启动方法:

ctx = mp.get_context('spawn')
p = ctx.Process(target=foo, args=(var,))

但是请准备好使用不同于 fork 的其他启动方法来支付大量的时间损失。我在我的机器 运行 Ubuntu 18.04 上启动一个 python 进程进行了基准测试,其中:

  • 分叉 1.59 毫秒
  • forkserver 289.83 毫秒
  • 生成 348.20 毫秒

但这不一定与您的用例相关。