Surprising output using Parallel gem with srand and rand

I'm working with ruby 2.4.1 + parallel 1.11.2. I'm running the following in irb:

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

My understanding is that when in_processes is specified, Parallel.map forks the process and then executes the loop body. Given that, I expect both processes to have the same global state and therefore I had expected that both would output the same random number. However, here's what I get:

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]

For the record, if I execute srand(1) and then rand , I get 0.417022004702574, so it appears that neither process got the random number seed which I set. I can get the behavior I want by setting the random number seed within the loop, but before I do that I'm trying to understand why it doesn't work to put the seed outside the loop.

I'm trying to make sense of this situation. Is this behavior somehow specific to the random number generator, so I wouldn't necessarily have the same problem (namely, expected shared initial state and didn't get it) with other objects? Or is Parallel not really having the same effect as a plain fork system call?

The documentation about Parallel with in_processes led me to believe that it acted like fork , but that doesn't seem to be the case here, hence my surprise.

EDIT: Some more experimenting shows that the same behavior arises when using Process.fork , so the problem has to do with fork and not the 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

EDIT: Further investigation seems to show that the option isolation: true is relevant here. When accessing a variable in the parent process, isolation: true seems to have the desired effect.

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]

But isolation: true doesn't seem to have the desired effect on rand . Still don't understand what's going on there.

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]

Don't use rand() which depends on global state. Instead use SecureRandom or if you need predictable sequences, 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

This gives consistent output:

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

This is just one more reason why you shouldn't use rand() .

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

上一篇: 将本地TFS迁移到Team Foundation Service

下一篇: 使用srand和rand的并行gem令人惊讶的输出