我 fork 了几个进程,我需要每个进程对随机选择的项目执行任务。 我发现 fork 进程选择完全相同的随机数。 我尝试生成种子并调用 srand(),但没有多大帮助。实际上,我读过的大多数文档都建议避免使用 srand() “除非您确切知道自己在做什么”。
这是我的代码:
#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Long qw(GetOptions);
use Time::HiRes qw(time);
use MongoDB;
#use Math::Random::Secure qw(rand);
my $num_clients = shift;
my $num_loops = 50_000_000;
sub test_forked_sub {
my $sum=0;
my $random_offset = rand(120);
printf "time: %10.4f - Random offset: %6.2f at pid: %s \n", time(), $random_offset, $$;
for (my $i=0; $i < $num_loops ; $i++) {
$sum += rand(120);
}
}
my $nub_processes = 0;
for (my $client_id =0; $client_id < $num_clients; $client_id++ ) {
if (my $pid = fork) { #Parent
$nub_processes++;
}
else { # child
die "cannot fork: $!" unless defined $pid;
test_forked_sub();
exit(0);
}
}
while ($nub_processes){
wait;
$nub_processes--;
}
当我运行它时,我在每个进程中得到完全相同的“随机”数字:
$ time ./test_rand_fork.pl 10
time: 1569510011.6891 - Random offset: 46.64 at pid: 2091
time: 1569510011.6937 - Random offset: 46.64 at pid: 2092
time: 1569510011.6987 - Random offset: 46.64 at pid: 2093
time: 1569510011.7028 - Random offset: 46.64 at pid: 2094
time: 1569510011.7070 - Random offset: 46.64 at pid: 2095
time: 1569510011.7097 - Random offset: 46.64 at pid: 2096
time: 1569510011.7144 - Random offset: 46.64 at pid: 2097
time: 1569510011.7203 - Random offset: 46.64 at pid: 2098
time: 1569510011.7230 - Random offset: 46.64 at pid: 2099
time: 1569510011.7249 - Random offset: 46.64 at pid: 2100
real 0m3.974s
user 0m32.955s
sys 0m1.444s
一个可能但丑陋的解决方案,它不是 fork 。相反,从我的 shell 运行多个实例,如下所示:
# shell processes
$ for i in `seq 1 10`; do ./test_rand_fork.pl 1 & done
time: 1569511908.7708 - Random offset: 7.44 at pid: 4129
time: 1569511908.8070 - Random offset: 19.50 at pid: 4131
time: 1569511908.8068 - Random offset: 97.59 at pid: 4132
time: 1569511908.8073 - Random offset: 14.51 at pid: 4133
time: 1569511908.8077 - Random offset: 16.70 at pid: 4134
time: 1569511908.8080 - Random offset: 108.63 at pid: 4138
time: 1569511908.8079 - Random offset: 69.44 at pid: 4137
time: 1569511908.8080 - Random offset: 83.25 at pid: 4136
time: 1569511908.8080 - Random offset: 43.33 at pid: 4135
time: 1569511908.8203 - Random offset: 33.82 at pid: 4139
由于“shell 进程”是一个丑陋的解决方案,我尝试使用 Math::Random::Secure。 以下是我根据需要获得随机性的结果,但它比“shell 进程”方法慢 50 倍:
# using: Math::Random::Secure qw(rand);
$ time ./test_rand_fork.pl 10
time: 1569510036.9331 - Random offset: 112.48 at pid: 2128
time: 1569510036.9470 - Random offset: 47.15 at pid: 2129
time: 1569510036.9501 - Random offset: 20.77 at pid: 2130
time: 1569510036.9517 - Random offset: 40.98 at pid: 2131
time: 1569510036.9521 - Random offset: 13.84 at pid: 2132
time: 1569510036.9538 - Random offset: 20.43 at pid: 2133
time: 1569510036.9543 - Random offset: 48.48 at pid: 2134
time: 1569510036.9563 - Random offset: 109.29 at pid: 2135
time: 1569510036.9579 - Random offset: 70.30 at pid: 2136
time: 1569510036.9601 - Random offset: 24.31 at pid: 2137
real 3m17.251s
user 32m31.129s
sys 0m1.054s
我需要的随机性并不是出于安全目的,我只是需要良好的传播。 有没有一种方法可以为 fork 进程生成足够好的种子,并且仍然使用标准 rand() 或替代更快的方法?或者至少不需要安装额外的库?
最佳答案
确实是srand你要的那个。虽然确实一般情况下人们不需要花太多时间来处理它,但它有一些用途
However, there are a few situations where programs are likely to want to call
srand
. One is for generating predictable results, generally for testing or debugging. There, you usesrand($seed)
, with the same$seed
each time.
而且,正是为了您的目的
Another case is that you may want to call
srand
after afork
to avoid child processes sharing the same seed value as the parent (and consequently each other).
一个简单的例子
perl -wE'
say "Parent $$ rand: ", rand;
for (1..4) {
$pid = fork // die "Cant fork: $!";
if ($pid == 0) { srand(); say "\tchild $$ rand: ", rand; exit }
}'
打印
Parent 23375 rand: 0.502774688721733
child 23376 rand: 0.914527430039929
child 23377 rand: 0.97985713889739
child 23378 rand: 0.127702740327553
child 23379 rand: 0.187181734786467
正是上面的srand
发挥了作用;如果没有它,数字是相同的(因为它们必须是相同的)。请注意,它也可以使用种子运行,这为我们提供了更好的诊断/测试控制。
注意,避免传播坏习惯。
在上面的快速程序中,子进程会被系统清理(由init回收)。然而,一个人确实应该总是收获,所以上面应该确实有
for (1..4) {
$pid = fork // die "Cant fork: $!";
if ($pid == 0) { srand(); say "\tchild $$ rand: ", rand; exit }
push @procs, $pid;
}
for (@procs) { $gone = wait; say "$gone exited with $?" }
使用 waitpid 会更好和非阻塞以实现更好的监控,也许在 signal handler 中.
关于perl - 如何在 fork 进程中获取随机数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58120618/