Как я могу установить максимальное количество разветвленных детей?

Обновлено: Я пометил этот C в надежде получить больше ответов. Меня интересует больше теория, чем реализация на конкретном языке. Поэтому, если вы программист на C, рассматривайте следующий PHP как псевдокод и не стесняйтесь отвечать, написав на C.

Я пытаюсь ускорить сценарий PHP CLI, заставляя его выполнять свои задачи параллельно, а не последовательно. Задачи полностью независимы друг от друга, поэтому не имеет значения, в каком порядке они начинаются / заканчиваются.

Вот исходный сценарий (обратите внимание, что все эти примеры для ясности сокращены):

<?php

$items = range(0, 100);

function do_stuff_with($item) { echo "$item\n"; }

foreach ($items as $item) {
    do_stuff_with($item);
}

Мне удалось заставить его работать на $items параллельно с pcntl_fork(), как показано ниже:

<?php

ini_set('max_execution_time', 0); 
ini_set('max_input_time', 0); 
set_time_limit(0);

$items = range(0, 100);

function do_stuff_with($item) { echo "$item\n"; }

$pids = array();
foreach ($items as $item) {
    $pid = pcntl_fork();
    if ($pid == -1) {
        die("couldn't fork()");
    } elseif ($pid > 0) {
        // parent
        $pids[] = $pid;
    } else {
        // child
        do_stuff_with($item);
        exit(0);
    }   
}

foreach ($pids as $pid) {
    pcntl_waitpid($pid, $status);
}

Теперь я хочу расширить это так, чтобы одновременно было, скажем, максимум 10 детей. Как лучше всего с этим справиться? Я пробовал несколько вещей, но без особого успеха.

Ха! Мне нравится название - мне пришлось прийти и прочитать вопрос только потому, что название напомнило бездушных диктаторов, мстящих политическим злодеям, пытая своих детей вилками ...

BenAlabaster 03.12.2008 08:40

Ха. Что ж, я рад, что моя проблема в том, что она есть, а не в том, что вы думали :-)

skix 03.12.2008 08:46
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
3
2
2 392
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

мужчина 2 setrlimit

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

Как насчет того, чтобы я хотел сделать это на уровне приложения?

skix 03.12.2008 08:44

Это системный вызов, который вы можете вызвать из своего приложения. Он очень часто используется для ограничения ядра, дампов ядра, файловых дескрипторов и т. д. Вы также можете использовать его для ограничения загрузки ЦП (я так часто делаю) и количества процессов.

Dustin 03.12.2008 09:31

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

Разветвление - дорогостоящая операция. Судя по всему, вам действительно нужно multiзаправка, а не multiобработка. Разница в том, что потоки имеют гораздо меньший вес, чем процессы, поскольку потоки совместно используют виртуальное адресное пространство, но процессы имеют отдельные виртуальные адресные пространства.

Я не разработчик PHP, но быстрый поиск в Google показывает, что PHP изначально не поддерживает многопоточность, но для этой работы есть библиотеки.

В любом случае, как только вы выясните, как создавать потоки, вы должны выяснить, сколько потоков нужно создавать. Для этого вам нужно знать, в чем узкое место вашего приложения. Узким местом является процессор, память или ввод-вывод? Вы указали в своих комментариях, что вы привязаны к сети, а сеть - это тип ввода-вывода.

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

Если бы у вас была ограниченная память, многопоточность не помогла бы.

Поскольку вы привязаны к вводу-выводу, выяснить, сколько потоков нужно создать, немного сложнее. Если для обработки всех рабочих элементов требуется примерно одинаковое время с очень низкой дисперсией, вы можете оценить, сколько потоков нужно запустить, измерив, сколько времени занимает один рабочий элемент. Однако, поскольку сетевые пакеты, как правило, имеют сильно различающиеся задержки, это маловероятно.

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

Еще один вариант - отказаться от многопоточности / многопроцессорности и вместо этого просто выполнять асинхронный ввод-вывод. Поскольку вы упомянули, что работаете над одноядерным процессором, это, вероятно, будет самым быстрым вариантом. Вы можете использовать такие функции, как socket_select(), чтобы проверить, есть ли у сокета данные. Если это так, вы можете прочитать данные, в противном случае вы перейдете на другой сокет. Это требует гораздо большего учета, но вы избегаете ожидания поступления данных в один сокет, когда данные доступны в другом сокете.

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

$my_process_index = 0;
$pids = array();

// Fork off $max_procs processes
for($i = 0; $i < $max_procs - 1; $i++)
{
  $pid = pcntl_fork();
  if ($pid == -1)
  {
    die("couldn't fork()");
  }
  elseif ($pid > 0)
  {
    // parent
    $my_process_index++;
    $pids[] = $pid
  }
  else
  {
    // child
    break;
  }
}

// $my_process_index is now an integer in the range [0, $max_procs), unique among all the processes
// Each process will now process 1/$max_procs of the items
for($i = $my_process_index; $i < length($items); $i += $max_procs)
{
  do_stuff_with($items[$i]);
}

if ($my_process_index != 0)
{
  exit(0);
}

Спасибо за ответ, попробую. Я читал кое-где, что в Unix fork () на самом деле не так уж и дорого, и как в любом случае реализована многопоточность? Это устаревшая / неверная информация?

skix 03.12.2008 09:09

«Вы получите столько же параллелизма, сколько у вас ядер ЦП; больше потоков - и вы просто тратите время на переключение контекста». Что ж, в моем случае у меня только 1 ядро, но я могу значительно ускорить процесс, выполняя сразу 20 задач. Задачи зависят от (медленных) сетевых операций.

skix 03.12.2008 09:12

Нет, многопоточность реализована не так. Трудно объяснить с помощью 300 символов - прочтите о том, как реализована fork (), о виртуальной памяти, виртуальных адресных пространствах, копировании при записи и многих других связанных темах.

Adam Rosenfield 03.12.2008 09:16

fork () не дорого, exec () стоит. Ядро не копирует сегмент кода - его разделяют и родительский, и дочерний. Сегмент данных будет дублироваться только тогда, когда родитель или потомок что-то изменят в сегменте данных (копирование при записи). NB! Я предполагаю, что мы говорим здесь о Unix.

qrdl 03.12.2008 11:00
Ответ принят как подходящий

Системного вызова для получения списка дочерних идентификаторов PID нет, но ps может сделать это за вас.

Переключатель --ppid перечислит всех дочерних процессов для вашего процесса, поэтому вам просто нужно подсчитать количество строк, выводимых ps.

В качестве альтернативы вы можете поддерживать свой собственный счетчик, который вы будете увеличивать для fork() и уменьшать для сигнала SIGCHLD, предполагая, что ppid остается неизменным для обработки fork'ed.

Я решил пойти с SIGCHLD и wait (), чтобы отслеживать завершенные дочерние элементы. Спасибо за ответ.

skix 05.12.2008 03:35

@skix - Вы не рассказываете, как это выглядит?

Michele 26.03.2018 19:47

Другие вопросы по теме