使用srand和rand的并行gem令人惊讶的输出

我正在使用红宝石2.4.1 +并行1.11.2。 我在irb中运行以下内容:

require 'parallel'
srand(1)
Parallel.map([0, 1], in_processes: 2) { |i| puts "in process #{i}; rand => #{rand}" }

我的理解是,当指定in_processes时, Parallel.map分叉进程并执行循环体。 鉴于此,我预计这两个进程都具有相同的全局状态,因此我期望两者都输出相同的随机数。 但是,这是我得到的:

irb(main):003:0> Parallel.map([0, 1], in_processes: 2) { |i| puts "in process #{i}; rand => #{rand}" }
in process 1; rand => 0.48721687007281356
in process 0; rand => 0.7502824863668285
=> [nil, nil]

为了记录,如果我执行srand(1)然后rand ,我得到0.417022004702574,所以似乎没有进程获得我设置的随机数种子。 我可以通过在循环中设置随机数种子来获得我想要的行为,但在我这样做之前,我试图理解为什么它不能将种子放在循环之外。

我试图理解这种情况。 这种行为在某种程度上是随机数生成器所特有的,所以我不一定有与其他对象相同的问题(即预期的共享初始状态并且没有得到它)​​? 或者是Parallel与真正的fork系统调用没有相同的效果?

有关in_processes并行的文档使我相信它的行为像fork ,但在这里似乎并不是这样,因此我感到惊讶。

编辑:一些更多的实验显示,使用Process.fork时出现相同的行为,所以问题必须与fork而不是并行gem。

$ cat foo.rb
srand(1)
pid = Process.fork
if !pid
then puts "child says rand => #{rand}"
else puts "parent says rand => #{rand}"
Process.wait(pid)
end

$ ruby foo.rb
parent says rand => 0.417022004702574
child says rand => 0.7054895237863591

编辑:进一步的调查似乎表明,选项isolation: true在这里相关。 当访问父进程中的变量时, isolation: true似乎具有预期的效果。

irb(main):037:0> foo = 1;
irb(main):038:0* Parallel.map([0, 1, 2, 3, 4, 5], in_processes: 2) { |i| puts "in process #{i}; foo = #{foo}"; foo = foo + 1 }
in process 0; foo = 1
in process 2; foo = 2
in process 3; foo = 3
in process 4; foo = 4
in process 5; foo = 5
in process 1; foo = 1
=> [2, 2, 3, 4, 5, 6]
irb(main):039:0> foo = 1;
irb(main):040:0* Parallel.map([0, 1, 2, 3, 4, 5], in_processes: 2, isolation: true) { |i| puts "in process #{i}; foo = #{foo}"; foo = foo + 1 }
in process 1; foo = 1
in process 0; foo = 1
in process 2; foo = 1
in process 3; foo = 1
in process 4; foo = 1
in process 5; foo = 1
=> [2, 2, 2, 2, 2, 2]

isolation: true似乎没有rand预期的效果。 仍然不明白那里发生了什么。

irb(main):032:0> srand(1);
irb(main):033:0* Parallel.map([0, 1], in_processes: 2) { |i| puts "in process #{i}; rand => #{rand}" }
in process 0; rand => 0.6837528723167413
in process 1; rand => 0.1469087219402977
=> [nil, nil]
irb(main):034:0> srand(1);
irb(main):035:0* Parallel.map([0, 1], in_processes: 2) { |i| puts "in process #{i}; rand => #{rand}" }
in process 0; rand => 0.7906373908366543
in process 1; rand => 0.8807214141308389
=> [nil, nil]

不要使用依赖于全局状态的rand() 。 而是使用SecureRandom或者如果您需要可预测的序列,则Random

seed = 1
generators = Array.new(2) { Random.new(seed) }

Parallel.map([0, 1], in_processes: 2) do |i|
  puts "in process #{i}; rand => #{generators[i].rand}"
end

这提供了一致的输出:

in process 1; rand => 0.417022004702574
in process 0; rand => 0.417022004702574

这只是你不应该使用rand()另一个原因。

链接地址: http://www.djcxy.com/p/68041.html

上一篇: Surprising output using Parallel gem with srand and rand

下一篇: Finding the Seed of the rand() Function TI