I have been build some background apps recently which need to do a lot of repetitions (think 100,000 times the same task which takes about 0.25 to 1 second to execute, of which most of it is waiting for the remote server to respond). Since these are daily and are not that server intensive it made sense to run them concurrently so instead of taking the good part of the day it just takes a few hours. I noticed on the proc_open page there was a very nice solution by Matou Havlena of havlena.net.
I have modified the solution a little to make it easier to read and made the output optional.
If you want to fork the solution it is on GitHub:
https://github.com/dalehurley/PHP-Process-Manager
Alternatively you can download the code:
https://dalehurley.com/playground/process_manager.zip
If you want to see a basic example:
https://dalehurley.com/playground/example.php (warning this takes a little while to load)
class Processmanager { public $executable = "php"; //the system command to call public $root = ""; //the root path public $processes = 3; //max concurrent processes public $sleep_time = 2; //time between processes public $show_output = false; //where to show the output or not private $running = array();//the list of scripts currently running private $scripts = array();//the list of scripts - populated by addScript private $processesRunning = 0; //count of processes running function addScript($script, $max_execution_time = 300) { $this->scripts[] = array("script_name" => $script, "max_execution_time" => $max_execution_time); } function exec() { $i = 0; for(;;) { // Fill up the slots while (($this->processesRunning<$this->processes) and ($i<count($this->scripts))) { if($this->show_output) { ob_start(); echo "<span style='color: orange;'>Adding script: ".$this->scripts[$i]["script_name"]."</span><br />"; ob_flush(); flush(); } $this->running[] =& new Process($this->executable, $this->root, $this->scripts[$i]["script_name"], $this->scripts[$i]["max_execution_time"]); $this->processesRunning++; $i++; } // Check if done if (($this->processesRunning==0) and ($i>=count($this->scripts))) { break; } // sleep, this duration depends on your script execution time, the longer execution time, the longer sleep time sleep($this->sleep_time); // check what is done foreach ($this->running as $key => $val) { if (!$val->isRunning() or $val->isOverExecuted()) { if($this->show_output) { ob_start(); if (!$val->isRunning()) { echo "<span style='color: green;'>Done: ".$val->script."</span><br />"; } else { echo "<span style='color: red;'>Killed: ".$val->script."</span><br />"; } ob_flush(); flush(); } proc_close($val->resource); unset($this->running[$key]); $this->processesRunning--; } } } } } class Process { public $resource; public $pipes; public $script; public $max_execution_time; public $start_time; function __construct(&$executable, &$root, $script, $max_execution_time) { $this->script = $script; $this->max_execution_time = $max_execution_time; $descriptorspec = array( 0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w') ); $this->resource = proc_open($executable." ".$root.$this->script, $descriptorspec, $this->pipes, null, $_ENV); $this->start_time = mktime(); } // is still running? function isRunning() { $status = proc_get_status($this->resource); return $status["running"]; } // long execution time, proccess is going to be killer function isOverExecuted() { if ($this->start_time+$this->max_execution_time<mktime()) return true; else return false; } }
Quick Start – Load the process manager and some scripts:
include_once('class.process_manager.php'); //load the class file $manager = new Processmanager(); //create the manager object $manager->executable = "php"; //the Linux executable $manager->path = ""; //path to the scripts to run $manager->show_output = true; //show the output of the manager $manager->processes = 3; //max concurrent processes $manager->sleep_time = 1; //time between checking if the processes are complete $manager->addScript("sleep.php", 2); //add a script and it max execution time in seconds $manager->addScript("sleep.php", 2); $manager->addScript("sleep.php", 1); $manager->addScript("sleep.php", 4); $manager->addScript("sleep.php", 5); $manager->addScript("sleep.php"); //no max execution time defaults to 300 seconds $manager->exec(); //start processing through the code echo 'Completed all tasks';
Acknowledgements
Matou Havlena of havlena.net came up with the original concept and posted it on PHP.NET
Thanks for giving the information. It will help me lot.