2013-09-11 2 views
1

Я использую компонент Symfony2 Process для управления пулом процессов вручную.Symfony2 Process component - невозможно создать канал и запустить новый процесс

В приведенном ниже примере я перезапускаю 2 простых процесса каждые 2 секунды и отслеживаю, что происходит. Приложение прерывается после перезапуска этих процессов несколько сотен раз.

Выполнение останавливается, и я получаю следующее предупреждение PHP:

proc_open(): unable to create pipe Too many open files 

, а затем следующее исключение выбрасывается компонентом Symfony процесса:

[Symfony\Component\Process\Exception\RuntimeException] 
Unable to launch a new process. 

Я вручную контролировать общее количество открытых процессов, и он никогда не поднимается до ожидаемого предела.

Ниже упрощена фрагмент является частью команды Symfony2 и будучи запущенной из командной строки (например, приложение/консоль хомяк: бег):

$processes[] = new Process("ls > /dev/null", null, null, null, 2); 
    $processes[] = new Process("date > /dev/null", null, null, null, 2); 

    while (count($processes) > 0) { 
     foreach ($processes as $i => $process) { 
      if (!$process->isStarted()) { 
       $process->start(); 

       continue; 
      } 

      try { 
       $process->checkTimeout(); 
      } catch (\Exception $e) { 
       // Don't stop main thread execution 
      } 

      if (!$process->isRunning()) { 
       // All processes are timed out after 2 seconds and restarted afterwards 
       $process->restart(); 
      } 
     } 

     usleep($sleep * 1000000); 
    } 

Это приложение выполняется на сервере MAC под управлением OS X 10.8.4.

Буду признателен за то, как преследовать корень этой проблемы.

Update # 1: я упростил свою функцию, чтобы работать с основными командами, как ls и date для быстрого тестирования. По-прежнему выглядит, что команда Process не работает после запуска и остановки около 1000-1500 процессов.

Я подозревал, что proc_close() не был вызван правильно для каждого процесса, но дальнейшее расследование показало, что здесь не так.

ответ

2

Файловые дескрипторы не мусора, поэтому они в конечном итоге заполнить (ограничение ОС или ограничения PHP, не уверен), но вы можете это исправить, добавив explicit garbage collection call:

gc_collect_cycles(); 
usleep($sleep * 1000000); 

Также, forwarned что сбор мусора не очень хорошо работает внутри цикла foreach из-за того, что php сопоставляет временные переменные массива $foo as $bar => $var в памяти. Если вам это нужно в той части кода, вы можете переключить его на что-то вроде этого, вместо, который я думаю должен позволить для сбора мусора внутри цикла for:

$processes[] = new Process("ls > /dev/null", null, null, null, 2); 
$processes[] = new Process("date > /dev/null", null, null, null, 2); 

$sleep = 0; 

do { 
    $count = count($processes); 
    for($i = 0; $i < $count; $i++) { 
     if (!$processes[$i]->isStarted()) { 
      $processes[$i]->start(); 

      continue; 
     } 

     try { 
      $processes[$i]->checkTimeout(); 
     } catch (\Exception $e) { 
      // Don't stop main thread execution 
     } 

     if (!$processes[$i]->isRunning()) { 
      // All processes are timed out after 2 seconds and restarted afterwards 
      $processes[$i]->restart(); 
     } 

     gc_collect_cycles(); 
    } 

    usleep($sleep * 1000000); 
} while ($count > 0); 
+0

Хороший вопрос. Я не рассматривал необходимость ручного вызова сборщика мусора. Выполнение некоторых дополнительных тестов (перезапуск процессов 100 000 раз) с помощью 'gc_collect_cycles()' подтвердил ваше решение. Благодаря! – ukliviu

+0

лучшим способом было бы - - stop (0) 'экземпляр процесса, когда это было сделано, это эффективно закрыло бы дескрипторы открытого файла, не полагаясь на' __destruct', вызываемый сборщиком мусора. – Florian

Смежные вопросы