我分叉了几个流程,我需要每个流程来对随机选择的项目执行任务。我发现分叉的进程选择完全相同的随机数。我尝试生成种子并调用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
一个可能但丑陋的解决方案,不要分叉。相反,要从我的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
我需要的随机性不是出于安全目的,我只需要一个良好的扩展即可。有没有一种方法可以为分支过程生成足够好的种子,而仍然使用标准的rand()或其他更快的方法?还是至少不需要安装其他库?
您确实很聪明。虽然的确是这样,但确实有一些用途
但是,在某些情况下,程序可能要调用
srand
。一种是生成可预测的结果,通常用于测试或调试。在那里,你用srand($seed)
,用相同的$seed
各一次。
并且正是出于您的目的
另一种情况是,您可能希望
srand
在a之后调用,fork
以避免子进程与父进程共享相同的种子值(因此彼此共享)。
一个快速的例子
perl -wE' say "Parent $$ rand: ", rand; for (1..4) { $pid = fork // die "Cant fork: $!"; if ($pid == 0) { srand(); say "\tchild $$ rand: ", rand; exit } }'
版画
perl -wE' say "Parent $$ rand: ", rand; for (1..4) { $pid = fork // die "Cant fork: $!"; if ($pid == 0) { srand(); say "\tchild $$ rand: ", rand; exit } }'
正是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和non-blocking更好地监视,并且可能在信号处理程序中更好。