Запустите несколько команд exec сразу (но подождите, пока последний не завершится)

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

У меня есть информация, которая передается моей функции через запрос _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); } 

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

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

Есть идеи?

Благодаря!!!

Solutions Collecting From Web of "Запустите несколько команд exec сразу (но подождите, пока последний не завершится)"

Если вы поместите вызов exec() в отдельный скрипт, вы можете вызывать этот внешний скрипт несколько раз параллельно с помощью curl_multi_exec() . Таким образом, вы делаете все вызовы отдельными запросами, чтобы они могли выполняться одновременно. Poll &$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 

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

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

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

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

К концу строки выполнения.

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

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

Заметка:

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

Таким образом, вы можете выполнить фоновый рисунок, если вы перенаправляете вывод в файл:

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

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

 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-скрипт будет вызываться после каждого execs script.tcl.

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

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

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

Посмотрите 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"; } }