LoginSignup
19
19

More than 5 years have passed since last update.

PCNTL関数を使用したプロセス制御でマルチプロセス(?)を実現するサンプル

Last updated at Posted at 2014-03-31

PCNTL関数を使用したプロセス制御でマルチプロセス(?)を
実現するサンプルです。

参考にさせていただいたサイト(コード)

実装結果

processctrl.class.php
<?php
declare(ticks = 1);

class Processctrl {

    protected $_max_process = 3;

    protected $_timeout = 5;

    protected $_args;

    protected $_stack;

    static public function getInstace()
    {
        static $object;
        if (is_null($object)) {
            $object = new static;
        }

        return $object;
    }

    public function __construct()
    {
        $this->_args = array();
        $this->_stack = array();

        pcntl_signal(SIGALRM, function ($signal) {
            //Log::debug("Get signal [".$signal."]");
            switch ($signal) {
                case SIGALRM:
                    echo ">>>> TIMEOUT!! <<<<\n";
                    exit();
                    break;
                default:
                    break;
            }
        });
    }

    public static function setMaxProcess($max_process)
    {
        static::getInstace()->_max_process = $max_process;
    }

    public static function setTimeout($sec)
    {
        static::getInstace()->_timeout = $sec;
    }

    public function addArgs($args)
    {
        $this->_args[] = $args;
    }

    public function clearArgs()
    {
        $this->_args = array();
    }

    public function run_all(callable $callback)
    {
        if (!is_callable($callback)) {
            throw new \Exception('Not callable['.$callback.']');
        }

        // 子プロセスを生成し処理を実行する
        foreach ($this->_args as $args) {
            $pid = pcntl_fork();
            if (-1 === $pid) {
                throw new \Exception('False fork process ['.pcntl_get_last_error().']');
            }

            if ($pid) {
                //Log::debug("[" . __METHOD__ . "]: Parent process PID[".$pid."].");
                $this->_stack[$pid] = true;
                if (count($this->_stack) >= $this->_max_process) {
                    //Log::debug("[" . __METHOD__ . "]: Stacked process is max ...waiting...[".count($this->_stack)."].");
                    unset($this->_stack[pcntl_waitpid(-1, $status, WUNTRACED)]);
                }
            } else {
                //Log::debug("[" . __METHOD__ . "]: Child process running.");
                pcntl_alarm($this->_timeout);
                call_user_func_array($callback, $args);
                exit();
            }
        }

        // すべての子プロセスの終了を待つ
        while (count($this->_stack) > 0) {
            //Log::debug("[" . __METHOD__ . "]: Waiting process all end...[".count($this->_stack)."].");
            unset($this->_stack[pcntl_waitpid(-1, $status, WUNTRACED)]);
        }
    }
}

【補足】
declare(ticks = 1);
↑pcntl_signalを使用するために必要な記述。(ticksとか説明読んでもよくわかりません)
http://www.php.net/manual/ja/intro.pcntl.php
『…PCNTLはシグナルハンドルコールバックの仕組みとしてticksを使用しており…』

使用テスト(※PHP 5.4.11で実行しています)

尚、clearArgs()を忘れてaddArgs()とrun_all()を繰り返すと残念な事態になります。(なりました)

exec_multi_process_sample.php
<?php

require_once('./processctrl.class.php');

date_default_timezone_set('Asia/Tokyo');

$pctrl = Processctrl::getInstace();

// 【その1】
// 同時プロセス数は3に設定して
// 処理内容はsleep(2)を実行するだけ
$pctrl->setMaxProcess(3);

$pctrl->addArgs(array(1));
$pctrl->addArgs(array(2));
$pctrl->addArgs(array(3));
$pctrl->addArgs(array(4));
$pctrl->addArgs(array(5));
$pctrl->addArgs(array(6));
$pctrl->addArgs(array(7));
$pctrl->addArgs(array(8));

echo ">>> Run-1\n";
$pctrl->run_all(function($num) {
    $date = new DateTime("now");
    printf("$num SLEEP(02)[%s]\n", $date->format('Y-m-d H:i:s'));
    sleep(2);
    $date = new DateTime("now");
    printf("$num END[%s]\n", $date->format('Y-m-d H:i:s'));
});

// 【その2】
// 同時プロセス数は5に設定して、タイムアウト時間を6に設定
// 処理内容はsleep(5)を実行するだけ(∴タイムアウトしない)
// ※clearArgs()していないので「その1」で設定した引数で実行される

$pctrl = Processctrl::getInstace();
$pctrl->setMaxProcess(5);
$pctrl->setTimeout(6);

echo ">>> Run-2\n";
$pctrl->run_all(function($num) {
    $date = new DateTime("now");
    printf("$num SLEEP(05)[%s]\n", $date->format('Y-m-d H:i:s'));
    sleep(5);
    $date = new DateTime("now");
    printf("$num END[%s]\n", $date->format('Y-m-d H:i:s'));
});

// 【その2】
// 同時プロセス数は2に設定して、タイムアウト時間を5に設定
// 処理内容はsleep(10)を実行するだけ(∴タイムアウトする)
// ※clearArgs()して新たに引数をセット

$pctrl->clearArgs();
$pctrl->setMaxProcess(2);
$pctrl->setTimeout(5);

$pctrl->addArgs(array(1));
$pctrl->addArgs(array(2));
$pctrl->addArgs(array(3));
$pctrl->addArgs(array(4));

echo ">>> Run-3\n";
$pctrl->run_all(function($num) {
    $date = new DateTime("now");
    printf("$num SLEEP(10)[%s]\n", $date->format('Y-m-d H:i:s'));
    sleep(10);
    $date = new DateTime("now");
    printf("$num END[%s]\n", $date->format('Y-m-d H:i:s'));
});
実行結果
>>> Run-1
1 SLEEP(02)[2013-05-06 17:37:21]
2 SLEEP(02)[2013-05-06 17:37:21]
3 SLEEP(02)[2013-05-06 17:37:21]
1 END[2013-05-06 17:37:23]
2 END[2013-05-06 17:37:23]
3 END[2013-05-06 17:37:23]
4 SLEEP(02)[2013-05-06 17:37:23]
5 SLEEP(02)[2013-05-06 17:37:23]
6 SLEEP(02)[2013-05-06 17:37:23]
4 END[2013-05-06 17:37:25]
5 END[2013-05-06 17:37:25]
6 END[2013-05-06 17:37:25]
7 SLEEP(02)[2013-05-06 17:37:25]
8 SLEEP(02)[2013-05-06 17:37:25]
7 END[2013-05-06 17:37:27]
8 END[2013-05-06 17:37:27]

>>> Run-2
1 SLEEP(05)[2013-05-06 17:37:27]
2 SLEEP(05)[2013-05-06 17:37:27]
3 SLEEP(05)[2013-05-06 17:37:27]
4 SLEEP(05)[2013-05-06 17:37:27]
5 SLEEP(05)[2013-05-06 17:37:27]
1 END[2013-05-06 17:37:32]
2 END[2013-05-06 17:37:32]
3 END[2013-05-06 17:37:32]
4 END[2013-05-06 17:37:32]
5 END[2013-05-06 17:37:32]
6 SLEEP(05)[2013-05-06 17:37:32]
7 SLEEP(05)[2013-05-06 17:37:32]
8 SLEEP(05)[2013-05-06 17:37:32]
6 END[2013-05-06 17:37:37]
7 END[2013-05-06 17:37:37]
8 END[2013-05-06 17:37:37]

>>> Run-3
1 SLEEP(10)[2013-05-06 17:37:37]
2 SLEEP(10)[2013-05-06 17:37:37]
1 END[2013-05-06 17:37:42]
>>>> TIMEOUT!! <<<<
2 END[2013-05-06 17:37:42]
>>>> TIMEOUT!! <<<<
3 SLEEP(10)[2013-05-06 17:37:42]
4 SLEEP(10)[2013-05-06 17:37:42]
3 END[2013-05-06 17:37:47]
>>>> TIMEOUT!! <<<<
4 END[2013-05-06 17:37:47]
>>>> TIMEOUT!! <<<<
19
19
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
19