我正在使用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]
答案 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()
的另一个原因。