Как получить случайное число в разветвленных процессах? - PullRequest
1 голос
/ 26 сентября 2019

Я разрабатываю несколько процессов, и мне нужно, чтобы каждый процесс выполнял задачу со случайно выбранными элементами.Я обнаружил, что разветвленные процессы выбирают одинаковые случайные числа.Я попытался создать семена и вызвать 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 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

Поскольку «процессы оболочки» - ужасное решение, я попытался использовать Math :: Random :: Secure.Вот результаты, которые я получаю случайным образом по мере необходимости, но он в 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 () или альтернативный более быстрый способ?или хотя бы то, что не требует установки дополнительной библиотеки?

1 Ответ

4 голосов
/ 26 сентября 2019

Это действительно и , что вы хотите.Хотя это правда, что, как правило, с этим не стоит обижаться, есть несколько вариантов использования

Однако в некоторых ситуациях программы могут вызывать srand.Один для генерации предсказуемых результатов, как правило, для тестирования или отладки.Там вы используете srand($seed), с одним и тем же $seed каждый раз.

и, в точности, для ваших целей

В другом случае вы можете вызвать srand после fork, чтобы избежать дочерних процессов, совместно использующих одно и то же начальное значениев качестве родителя (и, следовательно, друг друга).

Быстрый пример

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 , возможно, неблокирующее для лучшего мониторинга, и, возможно, в обработчик сигнала .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...