【问题标题】:Calling rand/mt_rand on forked children yields identical results在分叉的孩子上调用 rand/mt_rand 会产生相同的结果
【发布时间】:2013-01-30 12:14:38
【问题描述】:

我正在编写一个需要在 PHP 中执行并发任务的脚本。

我做了一个小测试,结果很奇怪。我正在使用 pcntl_fork 生成一个孩子。父进程什么也不做,只是等待子进程完成。

我正在生成 5 个孩子,每个孩子都运行一个函数,该函数会生成一个随机数(秒数)并休眠这么长时间。出于某种原因 - 所有孩子都生成相同的数字。

这是一个代码示例:

private $_child_count = 0;

private function _fork_and_exec($func)
{
    $cid = ++$this->_child_count;
    $pid = pcntl_fork();
    if ($pid){  // parent
        return $pid;
    } else {    // child
        $func($cid);
        //pcntl_waitpid(-1, $status);
        exit;
    }
}
public function parallel_test()
{
    $func = function($id){
        echo 'child ' . $id . ' starts'."\n";
        $wait_time = mt_rand(1,4);
        echo 'sleeping for '.$wait_time."\n";
        sleep($wait_time);
        echo 'child ' . $id . ' ends'."\n";
    };
    $children = [];
    for ($i=0; $i<5; $i++){
        $children[] = $this->_fork_and_exec($func) ."\n";
    }
    pcntl_wait($status);
    echo 'done' ."\n";
    exit;
}

示例输出:

child 1 starts
sleeping for 1
child 2 starts
sleeping for 1
child 3 starts
sleeping for 1
child 4 starts
sleeping for 1
child 5 starts
sleeping for 1
child 1 ends
child 2 ends
child 3 ends
child 4 ends
child 5 ends
done

提前致谢

【问题讨论】:

标签: php random fork


【解决方案1】:

这是因为所有子节点都以相同的状态开始(fork() 复制了代码和数据段)。并且由于 rand 和 mt_rand 是 pseudorandom 生成器,它们都会生成相同的序列。

您将不得不重新初始化随机生成器,例如使用进程/线程 ID 或从 /dev/urandom 读取几个字节。

【讨论】:

  • 要在这个答案的基础上再做一点,使用类似 mt_srand 的东西和线程 ID 来重新播种随机数生成器
【解决方案2】:

我真的认为你应该看看pthreads,它提供了与基于 Posix 线程的 PHP 兼容的多线程。

简单到

class AsyncOperation extends Thread {
    public function __construct($arg) {
        $this->arg = $arg;
    }
    public function run() {
        if ($this->arg) {
            echo 'child ' . $this->arg . ' starts' . "\n";
            $wait_time = mt_rand(1, 4);
            echo 'sleeping for ' . $wait_time . "\n";
            sleep($wait_time);
            echo 'child ' . $this->arg . ' ends' . "\n";
        }
    }
}
$t = microtime(true);
$g = array();
foreach(range("A","D") as $i) {
    $g[] = new AsyncOperation($i);
}
foreach ( $g as $t ) {
    $t->start();
}

输出

child B starts
sleeping for 3
child B ends
child C starts
sleeping for 3
child C ends
child A starts
sleeping for 4
child A ends
child D starts
sleeping for 4
child D ends

【讨论】:

  • 好的,更好的组织代码,OO,同步已经实现,我相信。谢谢
  • 你不需要fock100% POSIX ThreadsPHP中的任何东西
  • 为什么比分叉好?问题是我需要启用线程安全,并且我正在使用 nginx 运行 php-fpm,所以它被禁用了 atm,因为它不是必需的,所以我需要重新安装 php。在此之前我需要检查它在当前设置下是否存在性能问题
  • 分叉创建新进程,虽然它提供了更大的隔离,但它具有相当多的开销
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多