Pull to refresh

Параллельные вычисления, класс-обёртка для pcntl_fork()

Reading time 3 min
Views 7.1K
Хочу показать свой базовый класс, который я использую для PHP скриптов.
Приемущество его в том, что легко можно «распараллелить» работу.
Используется pcntl_fork() со всеми «вытекающими».

(тестировалось только на линукс)

Суть идеи:



class some_script extends CliScript
{
  protected function processWorker($item)
  {
      $this->log("I'm doing heavy job");
      sleep(rand(1,5));
      $this->log("I'm done doing heavy job");
  }

}

$script = new some_script();
$script->setWorkers(5);
$script->run();


в итоге получим один родительский процесс и 5 дочерних делающих «heavy job».

Есть свои нюансы и ограничения: открытые соединения с базой и файлы могут свести сума.

Нужен другой подход: родительский процесс должен заниматься базой и дочерние процессы делать «черную работу» и возвращать результат. Вот например:

class master_and_workers extends CliScript
{
    protected $contracts = array(2,4,5,1,3,7,3,1,4,9,2,4,1);
    protected $results;

    protected function processMaster()
    {
    foreach($this->contracts as $contract)
    {
      while!$this->canStartWorker() ) { sleep(1); };

      $this->startWorker($contract);
    }

    $this->waitForChildren();

    var_export($this->results);
    }

    protected function processWorker($item)
    {
    $this->log("I'm busy for {$item} seconds...");
    sleep($item);
    $this->log("Job is done.");

    return "Job is done. Sleep time was {$item}";
    }

    protected function processResult($result)
    {
    $this->results[] = $result;
    }



$script = new master_and_workers();
$script->setWorkers(3);
$script->run();


Быстрое пояснение, если кто запутался:

после старта в родительском процессе выполняется метод processMaster(), который стартует дочерние процессы.
В дочернем процессе выполняется метод processWorker().
То, что дочерний процесс возвращает — сохраняется во временном файле, после завершения вызывается метод processResult() в родителе и передается туда результат.

в классе CliScript есть несколько полезных методов:

getRunningTime() возвращает время выполнения от старта в секундах
countWorkers() возвращает количество дочерних процессов (имеет смысл только в родтельском процессе)
log() если задан файл CliScript::$file_log то логируется, если нет — на экран. Выводится информация о том, «кто сообщает»

Чтобы убить всю компанию «нежно» отправте сигнал SIGTERM родительскому процессу, там есть примитивный обработчик.

В завершение.



Не могу сказать что код обкатанный и вылизаный, скорее наоборот, но мне он очень помогает быстренько загрузить CPU работой когда надо.

исходник класса CliScript

Если у кого-то есть похожие наработки и готов поделится — буду очень рад.
Tags:
Hubs:
+23
Comments 47
Comments Comments 47

Articles