使用带有srand和rand

时间:2017-06-09 20:24:01

标签: ruby random parallel-processing

我正在使用ruby 2.4.1 + parallel 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有关,而与Parallel 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]

1 个答案:

答案 0 :(得分:-1)

请勿使用取决于全局状态的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()的另一个原因。