Запустите несколько команд exec одновременно (но дождитесь окончания последней) - PullRequest
11 голосов
/ 02 апреля 2012

Я искал это и не могу найти никого, кто пытается сделать именно то, что я есть.

У меня есть информация, которая передается моей функции через запрос _POST.На основании этих данных я запускаю команду exec для запуска сценария TCL определенное количество раз (с различными параметрами, основанными на переменной post).Прямо сейчас у меня есть exec в foreach, так что для его запуска требуется вечность (сценарий TCL занимает около 15 секунд, поэтому, если мне нужно выполнить его 100 раз, у меня есть небольшая проблема).Вот мой код:

    public function executeAction(){
    //code to parse the _POST variable into an array called devices

    foreach($devices as $devID){
        exec("../path/to/script.tcl -parameter1 ".$device['param1']." -parameter2 ".$device['param2'], $execout[$devID]);
    }
    print_r($execout);
}

Очевидно, что этот код является просто выдержкой с удаленными большими кусками, но, надеюсь, этого достаточно, чтобы продемонстрировать, что я пытаюсь сделать.

Мне нужно запуститьвсе руководители сразу, и мне нужно дождаться их завершения, прежде чем вернуться.Мне также нужен вывод всех скриптов, хранящихся в массиве с именем $ execout.

Есть идеи?

Спасибо !!!

Ответы [ 5 ]

6 голосов
/ 02 апреля 2012

Если вы поместите свой exec() вызов в отдельный скрипт, вы можете вызвать этот внешний скрипт несколько раз параллельно, используя curl_multi_exec().Таким образом, вы будете делать все вызовы в отдельных запросах, чтобы они могли выполняться одновременно.Опрос &$still_running, чтобы увидеть, когда все запросы завершены, после чего вы можете собрать результаты по каждому.

Обновление: Вот сообщение в блоге , подробно описывающее, что яОписываю.


Пример

Основываясь на сообщении в блоге, указанном выше, я собрал следующий пример.

Сценарий выполняется параллельно:

// waitAndDate.php

<?php
sleep((int)$_GET['time']);
printf('%d secs; %s', $_GET['time'], shell_exec('date'));

Параллельное выполнение скриптовых вызовов:

// multiExec.php

<?php
$start = microtime(true);

$mh = curl_multi_init();
$handles = array();

// create several requests
for ($i = 0; $i < 5; $i++) {
    $ch = curl_init();

    $rand = rand(5,25); // just making up data to pass to script
    curl_setopt($ch, CURLOPT_URL, "http://domain/waitAndDate.php?time=$rand");
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 30);

    curl_multi_add_handle($mh, $ch);
    $handles[] = $ch;
}

// execute requests and poll periodically until all have completed
$isRunning = null;
do {
    curl_multi_exec($mh, $isRunning);
    usleep(250000);
} while ($isRunning > 0);

// fetch output of each request
$outputs = array();
for ($i = 0; $i < count($handles); $i++) {
    $outputs[$i] = trim(curl_multi_getcontent($handles[$i]));
    curl_multi_remove_handle($mh, $handles[$i]);
}

curl_multi_close($mh);

print_r($outputs);
printf("Elapsed time: %.2f seconds\n", microtime(true) - $start);

Вот несколько выводов, которые я получил при запуске несколько раз:

Array
(
    [0] => 8 secs; Mon Apr  2 19:01:33 UTC 2012
    [1] => 8 secs; Mon Apr  2 19:01:33 UTC 2012
    [2] => 18 secs; Mon Apr  2 19:01:43 UTC 2012
    [3] => 11 secs; Mon Apr  2 19:01:36 UTC 2012
    [4] => 8 secs; Mon Apr  2 19:01:33 UTC 2012
)
Elapsed time: 18.36 seconds

Array
(
    [0] => 22 secs; Mon Apr  2 19:02:33 UTC 2012
    [1] => 9 secs; Mon Apr  2 19:02:20 UTC 2012
    [2] => 8 secs; Mon Apr  2 19:02:19 UTC 2012
    [3] => 11 secs; Mon Apr  2 19:02:22 UTC 2012
    [4] => 7 secs; Mon Apr  2 19:02:18 UTC 2012
)
Elapsed time: 22.37 seconds

Array
(
    [0] => 5 secs; Mon Apr  2 19:02:40 UTC 2012
    [1] => 18 secs; Mon Apr  2 19:02:53 UTC 2012
    [2] => 7 secs; Mon Apr  2 19:02:42 UTC 2012
    [3] => 9 secs; Mon Apr  2 19:02:44 UTC 2012
    [4] => 9 secs; Mon Apr  2 19:02:44 UTC 2012
)
Elapsed time: 18.35 seconds

Надеюсь, это поможет!

Примечание с одной стороны: убедитесь, что ваш веб-сервер может обрабатывать столько параллельных запросов.Если он обслуживает их последовательно или может обслуживать только очень мало одновременно, этот подход приносит вам мало или ничего.: -)

5 голосов
/ 02 апреля 2012

PHP-функция exec всегда будет ждать ответа от вашего выполнения. Однако вы можете отправить stdout & stderror процесса в / dev / null (в unix) и выполнить все эти сценарии практически мгновенно. Это можно сделать, добавив ..

 '> /dev/null 2>&1 &'

До конца строки исполнения.

Но! это означает, что они разветвляются и заканчивают обработку независимо. Может быть, стоит попросить их написать ответ где-нибудь. И вы могли бы создать слушателя, чтобы подобрать и обработать его.

1 голос
/ 02 апреля 2012

Вам нужно немного изменить свой скрипт

  1. Сохранить данные записи в сеансе
  2. Exec, сохранить результат в сессии
  3. Использовать перенаправление с помощью JavaScript
  4. Команда перенаправления Echo после возврата exec с тем же URL-адресом, но с добавлением добавочного индекса, например? Index = 99
  5. Когда индекс достигнет конца, покажите весь результат
1 голос
/ 02 апреля 2012

Цитирование PHP Документация:

Примечание:

Если программа запускается с этой функцией, чтобы она продолжала работать в фоновом режиме, выходные данные программы должны быть перенаправлены в файл или другой поток вывода. В противном случае PHP будет зависать, пока не закончится выполнение программы.

Итак, вы можете выполнить в фоновом режиме, если перенаправить вывод в файл:

exec("../path/to/script.tcl -parameter1 ".$device['param1']." -parameter2 ".$device['param2']." > outputfile.txt", $execout[$devID]);

Но если вы хотите подождать, пока ВСЕ исполняются, прежде чем продолжить, вы должны сделать обратный вызов из внешнего скрипта. Может быть так:

exec("../path/to/script.tcl -parameter1 ".$device['param1']." -parameter2 ".$device['param2']." > ../path/to/outputfile.txt; ".PHP_BINDIR.DIRECTORY_SEPARATOR."php ../path/to/callback.php", $execout[$devID]);

Таким образом, ваш скрипт callback.php будет вызываться после каждого запуска script.tcl.

Может быть, вы можете сделать что-нибудь с этими трюками.

0 голосов
/ 02 августа 2015

Посмотрите на ExecFuture иFutureIterator в библиотеке libputil:

https://secure.phabricator.com/book/libphutil/class/ExecFuture/

Он делает именно то, что вам нужно, с довольно хорошим синтаксисом:

$futures = array();
foreach ($files as $file) {
  $futures[$file] = new ExecFuture("gzip %s", $file);
}
foreach (new FutureIterator($futures) as $file => $future) {
  list($err, $stdout, $stderr) = $future->resolve();
  if (!$err) {
    echo "Compressed {$file}...\n";
  } else {
    echo "Failed to compress {$file}!\n";
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...