Недавно я попробовал pthreads и был приятно удивлен — это расширение, которое добавляет в PHP возможность работать с несколькими самыми настоящими потоками. Никакой эмуляции, никакой магии, никаких фейков — все по-настоящему.



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


Стоит отметить, что автор расширения, Joe Watkins, в своих статьях предупреждает, что многопоточность — это всегда не просто и надо быть к этому готовым.


Кто не испугался, идем далее.


Что такое pthreads


Pthreads — это объектно-ориентированное API, которое дает удобный способ для организации многопоточных вычислений в PHP. API включает в себя все инструменты, необходимые для создания многопоточных приложений. PHP-приложения могут создавать, читать, писать, исполнять и синхронизировать потоки с помощью объектов классов Threads, Workers и Threaded.


Что внутри pthreads


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


Threaded — основа pthreads, дает возможность параллельного запуска кода. Предоставляет методы для синхронизации и другие полезные методы.


Thread. Можно создать поток, отнаследовавшись от Thread и реализовав метод run(). Метод run() начинает выполняться, причем в отдельном потоке, в момент, когда вызывается метод start(). Это можно инициировать только из контекста, который создает поток. Объединить потоки можно тоже только в этом-же контексте.


Worker. Персистентное состояние, которое в большинстве случаев используется разными потоками. Доступно, пока объект находится в области видимости или до принудительного вызова shutdown().


Помимо этих классов есть еще класс Pool. Pool — пул (контейнер) Worker-ов можно использовать для распределения Threaded объектов по Worker-ам. Pool — наиболее простой и эффективный способ организовать несколько потоков.


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


Пример


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


Так давайте приступим. Для этого создадим провайдер данных MyDataProvider (Threaded), он будет один и общий для всех потоков.


/**
 * Провайдер данных для потоков
 */
class MyDataProvider extends Threaded
{
    /**
     * @var int Сколько элементов в нашей воображаемой БД
     */
    private $total = 2000000;

    /**
     * @var int Сколько элементов было обработано
     */
    private $processed = 0;

    /**
     * Переходим к следующему элементу и возвращаем его
     * 
     * @return mixed
     */
    public function getNext()
    {
        if ($this->processed === $this->total) {
            return null;
        }

        $this->processed++;

        return $this->processed;
    }
}

Для каждого потока у нас будет MyWorker (Worker), где будет храниться ссылка на провайдер.


/**
 * MyWorker тут используется, чтобы расшарить провайдер между экземплярами MyWork.
 */
class MyWorker extends Worker
{
    /**
     * @var MyDataProvider
     */
    private $provider;

    /**
     * @param MyDataProvider $provider
     */
    public function __construct(MyDataProvider $provider)
    {
        $this->provider = $provider;
    }

    /**
     * Вызывается при отправке в Pool.
     */
    public function run()
    {
        // В этом примере нам тут делать ничего не надо
    }

    /**
     * Возвращает провайдера
     * 
     * @return MyDataProvider
     */
    public function getProvider()
    {
        return $this->provider;
    }
}

Сама обработка каждой задачи пула, (пусть это будет некая ресурсоемкая операция), наше узкое горлышко, ради которого мы и затеяли многопоточность, будет в MyWork (Threaded).


/**
 * MyWork это задача, которая может выполняться параллельно
 */
class MyWork extends Threaded
{

    public function run()
    {
        do {
            $value = null;

            $provider = $this->worker->getProvider();

            // Синхронизируем получение данных
            $provider->synchronized(function($provider) use (&$value) {
               $value = $provider->getNext();
            }, $provider);

            if ($value === null) {
                continue;
            }

            // Некая ресурсоемкая операция
            $count = 100;
            for ($j = 1; $j <= $count; $j++) {
                sqrt($j+$value) + sin($value/$j) + cos($value);
            }
        }
        while ($value !== null);
    }

}

Обратите внимание, что данные из провайдера забираем в synchronized(). Иначе есть вероятность часть данных обработать более 1 раза, или пропустить часть данных.
Теперь заставим все это работать с помощью Pool.


require_once 'MyWorker.php';
require_once 'MyWork.php';
require_once 'MyDataProvider.php';

$threads = 8;

// Создадим провайдер. Этот сервис может например читать некие данные
// из файла или из БД
$provider = new MyDataProvider();

// Создадим пул воркеров
$pool = new Pool($threads, 'MyWorker', [$provider]);

$start = microtime(true);

// В нашем случае потоки сбалансированы. 
// Поэтому тут хорошо создать столько потоков, сколько процессов в нашем пуле.
$workers = $threads;
for ($i = 0; $i < $workers; $i++) {
    $pool->submit(new MyWork());
}

$pool->shutdown();

printf("Done for %.2f seconds" . PHP_EOL, microtime(true) - $start);

Получается довольно элегантно на мой взгляд. Этот пример я выложил на гитхаб.


Вот и все! Ну почти все. На самом деле есть то, что может огорчить пытливого читателя. Все это не работает на стандартном PHP, скомпилированным с опциями по умолчанию. Чтобы насладиться многопоточностью, надо, чтобы в вашем PHP был включен ZTS (Zend Thread Safety).


Настройка PHP


В документации сказано, что PHP должен быть скомпилирован с опцией --enable-maintainer-zts. Я не пробовал сам компилировать, вместо этого нашел пакет для Debian, который и установил себе.


sudo add-apt-repository ppa:ondrej/php-zts
sudo apt update
sudo apt-get install php7.0-zts php7.0-zts-dev

Таким образом у меня остался прежний PHP, который запускается из консоли обычным образом, с помощью команды php. Соответственно, веб сервер использует его-же. И появился еще один PHP, который можно запускать из консоли через php7.0-zts.


После этого можно ставить расширение pthreads.


git clone https://github.com/krakjoe/pthreads.git
./configure
make -j8
sudo make install
echo "extension=pthreads.so" > /etc/pthreads.ini
sudo cp pthreads.ini /etc/php/7.0-zts/cli/conf.d/pthreads.ini

Вот теперь все. Ну… почти все. Представьте, что вы написали мультипоточный код, а PHP на машине у коллеги не настроен соответствующим образом? Конфуз, не правда ли? Но выход есть.


pthreads-polyfill


Тут снова спасибо Joe Watkins за пакет pthreads-polyfill. Суть решения такова: в этом пакете содержатся те-же классы, что и в расширении pthreads, они позволяют выполниться вашему коду, даже если не установлено расширение pthreads. Просто код будет выполнен в один поток.
Чтобы это заработало, вы просто подключаете через composer этот пакет и больше ни о чем не думаете. Там происходит проверка, установлено ли расширение. Если расширение установлено, то на этом работа polyfill заканчивается. Иначе подключаются классы-”заглушки”, чтобы код работал хотя бы в 1 поток.


Проверим


Давайте теперь посмотрим, действительно ли обработка происходит в несколько потоков и оценим выигрыш от использования этого подхода.
Я буду менять значение $threads из примера выше и смотреть, что получается.


Информация о процессоре, на котором запускал тесты


$ lscpu
CPU(s):                8
Потоков на ядро:       2
Ядер на сокет:         4
Model name:            Intel(R) Core(TM) i7-4700HQ CPU @ 2.40GHz

Посмотрим диаграмму загрузки ядер процессора. Тут все соответствует ожиданиям.


$threads = 1


$threads = 1


$threads = 2


$threads = 2


$threads = 4


$threads = 4


$threads = 8


$threads = 8


А теперь самое главное, ради чего все это. Сравним время выполнения.


$threads Примечание Время выполнения, секунд
PHP без ZTS
1 без pthreads, без polyfill 265.05
1 polyfill 298.26
PHP с ZTS
1 без pthreads, без polyfill 37.65
1 68.58
2 26.18
3 16.87
4 12.96
5 12.57
6 12.07
7 11.78
8 11.62

Из первых двух строк видно, что при использовании polyfill мы потеряли примерно 13% производительности в этом примере, это относительно линейного кода на совсем простом PHP “без всего”.


Далее, PHP с ZTS. Не обращайте внимание на такую большую разницу во времени выполнения в сравнении с PHP без ZTS (37.65 против 265.05 секунд), я не пытался привести к общему знаменателю настройки PHP. В случае без ZTS у меня включен XDebug например.


Как видно, при использовании 2-х потоков скорость выполнения программы примерно в 1.5 раза выше, чем в случае с линейным кодом. При использовании 4-х потоков — в 3 раза.


Можно обратить внимание, что хоть процессор и 8-ядерный, время выполнения программы почти не менялось, если использовалось более 4 потоков. Похоже, это связано с тем, что физических ядра у моего процессора 4. Для наглядности изобразил табличку в виде диаграммы.



Резюме


В PHP возможна вполне элегантная работа с многопоточностью с использованием расширения pthreads. Это дает ощутимый прирост производительности.

Поделиться с друзьями
-->

Комментарии (45)


  1. vadimr
    17.05.2016 09:44

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


    1. mnv
      17.05.2016 09:52

      Да, исправил.


  1. parrker
    17.05.2016 09:52
    +2

    Вопрос от человека, ни разу не работавшего с потоками: есть ли у такого подхода еще какие-то преимущества, помимо производительности?


    1. DeLuxis
      17.05.2016 09:57

      Ресурсы более рационально тратятся. На многоядерных серверах будут загружены все ядра.


      1. Fesor
        17.05.2016 12:29
        +1

        Это собственно причина того что производительность улучшается. Но опять же нужно учитывать еще локи, переключение контекста… Словом в WEB оно не столь разумно как event loop в подавляющем большинстве случаев.


        1. DeLuxis
          17.05.2016 12:37
          +1

          Просто в веб да, но многие пишут всякие выгрузки на PHP и суют их уже в крон.
          Или например экспорт отчета в админке Excel файла, собирающийся из всяких под отчетов.
          Короче есть где применить, нужно просто иметь ввиду, что есть такая классная штука.


          1. Fesor
            17.05.2016 19:32

            всякие выгрузки на PHP и суют их уже в крон.

            а это не сетевое взаимодействие? Там простоев из-за сетевых запросов обычно больше чем CPU-time на работу самого пыха.


            Короче есть где применить, нужно просто иметь ввиду, что есть такая классная штука.

            Иметь в виду — конечно стоит. Но нужно так же знать о других вариантах (мультиплексирование потока выполнения, корутины, очереди + процессы) и выбирать из них. А так для тредов есть свои задачи.


    1. mnv
      17.05.2016 10:00
      +1

      У каждого подхода свои преимущества и недостатки. Подходы:


      • Создаем общедоступную очередь, например, на Beanstalk, RabbitMQ или Redis или еще на чем-нибудь. Создаем PHP скрипт, который будем запускать из консоли несколько раз, создавая нужное количество процессов. Это решение наиболее универсальное.
        • Плюсы. Хорошая масштабируемость на несколько серверов, отказоустойчивость.
        • Минусы. Может быть неудобно или непрозрачно с точки зрения архитектуры. Если в обработке данных несколько “узких горлышек”, то возможно, понадобится предусмотреть несколько очередей.
      • Создавать потоки через Curl, для такого решения есть даже проект на гитхабе.
        • Плюсы. Мне неизвестны.
        • Минусы. Ненадежно.
      • Использовать popen().
        • Плюсы. Просто с первого взгляда.
        • Минусы. Сложно организовать равномерную загрузку ядер. Трудности в создании общей очереди.
      • Написать собственное расширение для PHP и пользоваться им.
        • Плюсы. Можно сделать полный фен шуй.
        • Минусы. Затратно.
      • Воспользоваться расширением PCNTL. Насколько это удачное решение, возможно, кто-то расскажет в комментариях.
      • Воспользоваться готовым расширением pthreads.
        • Плюсы. Надежность. Можно прятать многопоточное поведение внутри модуля, не выносить на уровень архитектуры. Простота в создании общей очереди.
        • Минусы. Нельзя масштабировать на несколько серверов.


      1. parrker
        17.05.2016 11:56
        +1

        Благодарю за развернутый ответ!


      1. Fesor
        17.05.2016 13:03
        +1

        Все кроме последнего никакого отношения к потокам не имеет. Это порождение процессов.


        Минусы. Сложно организовать равномерную загрузку ядер. Трудности в создании общей очереди.

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


        Написать собственное расширение для PHP и пользоваться им.

        Все уже написано. Имеет смысл только написало аналог микротредов (корутины + пул тредов), но в теории это можно и на userland сделать.


        Воспользоваться расширением PCNTL

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


        Так же есть отдельные экстеншены для того что бы организовать общую память между процессами, так что можно добиться прикольных вещей имея при этом свое адресное пространство для каждого процесса (безопаснее) + немного общей для кэшей. Правда тут уже нужно опять же вводи локи или разбираться с lock-free программированием и тут я не уверен что это можно делать красиво в php.


        Воспользоваться готовым расширением pthreads.

        На самом деле для WEB в 95% случаев все упирается в эффективность работы с I/O и тут явный лидер корутины/event loop так как нет накладных расходов на переключение контекстов. А что бы эффективнее использовать ресурсы можно просто увеличить количество процессов.


        Треды хорошо подходят для каких-то массивных вычислений, хотя тут уже вопрос зачем нам PHP если мы можем написать многопоточную програмку на Си с векторизацией вычислений и получить 100x профита.


        Так что использование тредов в PHP я считаю экзотикой нежели чем-то важным и необходимым. Хотя понимать минусы использования тредов — это важно.


        1. zapimir
          17.05.2016 14:35
          +1

          хотя тут уже вопрос зачем нам PHP если мы можем написать многопоточную програмку на Си с векторизацией вычислений и получить 100x профита

          Ну например если весь проект на PHP, то зачем для одной задачи искать программера на Си. На первое время решения на PHP хватит с головой, тем более, если речь о PHP 7.


          1. Fesor
            17.05.2016 16:45

            На первое время решения на PHP хватит с головой, тем более, если речь о PHP 7.

            Если речь идет про объемные вычисления, они для начала должны легко паралелиться, иначе объем работ на паралелизацию может слихвой покрыть разницу php vs С.


            Если алгоритм легко паралелится — то никаких проблем но "первое время" может быстро закончится если мы зависим от количества данных и оно увиличивается. У меня был на проектике скриптик с k-means, написанный на коленке потому что так быстрее. Его "первое время" закончилось через 2 недели, когда обработка данных стала занимать по 5 минут на запуск (100КК итераций). Переход на PHP7 а потом на HHVM снизил время в 2 раза но с объемами данных этот "профит" быстро бы невилировался. Распаралелить его обошлось бы довольно дорого, в итоге просто применили другой алгоритм кластеризации реализованный на java (потому что готовый и потому что реализовывать его на PHP сильно дорого вышло бы).


        1. phantomd
          17.05.2016 14:39
          +1

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

          Если использовать расширение pthreads + PCNTL, то можно сократить количество процессов и выиграть в производительности


          1. Fesor
            17.05.2016 16:41

            На мой взгляд дешевле и быстрее такое сделать на PHP, чем на Си.

            На мой взгляд вам в вашей задаче потоки не нужны. Берем очередь, берем парочку процессов-воркеров обрабатывающих очередь, в каждом воркере будут крутиться корутины/event loop (amphp, reactphp, recoil). Итого имеет малое количество процессов, отсутствие оверхэда на создание потоков/переключение контекста, отсутствие блокировок, максимальную утилизацию CPU, максимальный перформанс. Ну и делать это даже проще чем на тредах.


  1. mib
    17.05.2016 10:31
    +1

    А если например нужно использовать mysqli?
    На пример: задача в том, чтобы в несколько потоков брать записи из одной таблицы, проводить манипуляции над данными, а результат записывать в другую таблицу в произвольном порядке?
    При этом потоки не должны читать одну и ту-же запись, но должны гарантированно прочитать все записи.
    Объект mysqli нельзя «расшаривать» между потоками.


    1. mnv
      17.05.2016 10:37

      Не пробовал, но в этом примере должно быть подходящее решение


    1. ollisso
      17.05.2016 10:44

      1. создаёте mysqli объект после разделения на потоки — у каждого потока будет своё подключение.
      2. делаете очередь в базе, там дополнительные столбцы: «бот который взял на обработку».
      в момент взятия задачи на обработку, делаете лок:

      update table set bot=BOT_ID where bot=0;
      

      3. В добавок, нужно сделать какой нибуть механизм, который будет разлочивать строки ботов которые зависли.


      1. mib
        17.05.2016 11:00

        Я вместо этого делал так: каждый поток читает все строки таблицы, но обрабатывает только те, которые должен.
        Ну там несложные вычисления: каждый поток знает свой номер и общее количество потоков, ну и отсчитывает каждую X строчку со сдвигом Y


      1. GennPen
        17.05.2016 11:49

        Делал примерно так же, но присваивал значение timestamp. И отдельным потоком просматривал все записи с значением меньше, чем «timestamp — время на таймаут», если такие появлялись — давал им еще несколько попыток обнуляя значение, затем блокировка записи с присвоением значения заведомо бОльшим.


      1. phantomd
        17.05.2016 15:32

        У меня было реализовано так, по некому группирующему параметру из источника данных создавалась задача, в отдельной таблице. Далее, при чтении из источника, полученная строка сразу же удалялась. Одновременно может работать любое количество потоков без пересечения. Я делал это на Redis, там своя специфика работы на сокетах и как такового persistent connection просто не существует. В MySQL таких проблем нет.
        Если не удалять записи, а устанавливать некий параметр, аля bot_id, то всё равно нужно как-то чистить обработанные записи, чтобы не перегружать таблицу.


        1. ollisso
          17.05.2016 16:44

          В вашем случае, есть возможность что задача будет удалена, хотя она не была выполнена (Т.е. создаём задачу, бот её сразу же забирает, и потом бот почему то падает.). Лучше удалять задачу после того как она взята и выполнена. Т.е. когда «берём задачу» — её лочим, но не удаляем. Когда сделали — удаляем.


  1. SamDark
    17.05.2016 11:50
    +1

    включен XDebug например

    Замерам и выводам тогда не стоит верить. XDebug искажает картину полностью.


    1. mnv
      17.05.2016 11:56

      Основные сравнения тут без XDebug. С XDebug только 2 довольно синтетических теста для оценки потерь от использования polyfill.


      1. SamDark
        17.05.2016 19:16

        Разве не для всех замеров по PHP с ZTS он был включен?


        1. mnv
          17.05.2016 19:32
          +1

          Был включен только для PHP без ZTS


          1. SamDark
            17.05.2016 20:06
            +1

            Ох, заработался...


  1. AlexLeonov
    17.05.2016 11:57
    +3

    Воспользоваться расширением PCNTL. Насколько это удачное решение, возможно, кто-то расскажет в комментариях.

    Это вполне себе удачное решение. Только вы должны понимать, что pcntl — это многопроцессность, а не многопоточность.

    Плюсы:
    • Процессы независимы, каждый из них выполняется изолированно, его время жизни никак не зависит от других процессов
    • Расширение pcntl работает везде и из коробки (кроме Windows по понятным причинам)


    Минусы:
    • Форк — не самая дешевая операция
    • N процессов требуют *N памяти
    • Межпроцессное взаимодействие вам нужно выстраивать самостоятельно
    • После форка дочерний процесс теряет контекст (подключения к файлам, БД, прочим ресурсам), его нужно восстанавливать


    В целом мне pcntl нравится и он находит своё применение


  1. akalend
    17.05.2016 12:19
    +3

    Похоже, это связано с тем, что физических ядра у моего процессора 4

    так и есть, все рекомендации сводятся к тому, чтоб запускать по одному потоку на ядро


    1. Fesor
      17.05.2016 13:05

      Это связано с переключением контекста. Чем больше потоков, тем чаще нам нужно переключаться, а операция эта не дешевая.


    1. m_z
      17.05.2016 13:35

      Всё зависит от того, чем потоки занимаются. У меня сейчас на 8 ядрах запущено 2600+ потоков и всё летает. А если 8 потоков заняты вычислениями, загружая каждое ядро на 100%, то, естественно, что не делай, они не начнут быстрее выполнять свою работу.


  1. akalend
    17.05.2016 12:20
    -1

    к сожалению это не применимо для работы с БД


    1. GrizliK1988
      17.05.2016 12:32
      +1

      Почему? Даже если открыть N коннектов и работать с каждыми из них по отдельности?


    1. Fesor
      17.05.2016 13:06

      Приминимо, просто нужно держать не один коннекшен к базе, а пул коннекшенов.


  1. GrizliK1988
    17.05.2016 12:31
    -2

    Бесспорно полезное расширение, однако надо всегда держать в уме, что PHP интерпретируемый язык, который итак имеет достаточно накладных расходов на выполнение своих скиптов. Так что распараллеливание может и дать достаточное ускорение работы, но при этом забрать существенно больше ресурсов чем ожидается, так как все обертка на posix threads все же будет добавлять некий оверхед. Так же, если есть нужна проводить тяжелые операции на сервере (которые требуют оптимизации распараллеливанием), то это повод задуматься как это в будущем будет развиваться и, возможно, стоит какую то часть функционала переписать в виде C расширения, например.


    1. Fesor
      17.05.2016 13:07

      так как все обертка на posix threads все же будет добавлять некий оверхед

      Незначительный, им можно принебречь. Но в остальном согласен, event loop справляется лучше, а если нужно нагрузить все ядра — просто делаем больше процессов.


  1. coh
    17.05.2016 13:43

    Все бы хорошо, если не большое количчество нюансов, ограничей и отличия работы PHP c pthreads от PHP как такового.
    Нельзя быть уверенным, что стандартная языковая конструкция будет работать корректно.
    Пример: https://github.com/krakjoe/pthreads/issues/52
    И многое другое, типа позднего статического связываня и наследования… Достаточно взглянуть https://github.com/krakjoe/pthreads/issues

    Как эксперимент, очень интересная библиотека. В продакшен?… врядли.


    1. coh
      17.05.2016 13:52

      Промахнулся со статическим связыванием, проблема со статическими свойствами.


  1. Metus
    17.05.2016 14:27
    +3

    pthreads хорош, но стоит упомянуть и про подводные камни.


    Есть возможности "пронаследовать" какую-то часть окружения в поток (причём, по умолчанию это не "ничего"), но в ряде случаев вылезают WTF. Например, если в мастере был подключен автозагрузчик композера через require_once, он не подключится в потоке аналогичном образом.


    Ресурсы, файловые дескрипторы и прочее не шарятся. Потому лучше стараться запускать потоки через PTHREADS_INHERIT_NONE, а внутри производить собственные подключения к БД, логи (монолог умеет писать с блокировками) и т.д.


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


    Если вы захотите вернуть из потока вложенный массив и сделаете так.


    $this->result = ['hello' => ['foo' => 'bar']]

    То можете словить ошибку, так как это будет преобразовано в Volatile-объекты и при попытке считать данные в мастере они будут уже уничтожены сборщиком мусора.


    Самый простой "способ" в таком случае, это явно приводить к массиву:


    $this->result = (array) ['hello' => ['foo' => 'bar']]

    Подробнее здесь: http://stackoverflow.com/questions/14796674/a-php-pthreads-thread-class-cant-use-array


    В целом, от меня общий совет — каждый поток должен быть максимально независим от мастер процесса. Обмен, по возможности, производить скалярными данными.


    Также на некоторых конфигурациях систем (например у меня такое происходит Debian 7 и pthreads 3) могут вылетать ошибки сегментирования. С чем это точно связано я не знаю, но скорее всего с версиями каких-то библиотек.


  1. ArthurKushman
    17.05.2016 16:53

    Молодец Николай, редкую тему поднял (в контексте php/pthreads) и с конкретными примерами — привет от бывшего коллеги.


  1. parpalak
    17.05.2016 22:25
    +1

    Коля, я бы добавил на графики шкалу времени, чтобы был понятен масштаб. Вообще, это типичный вопрос по графикам — что отложено по осям :)


    1. mnv
      17.05.2016 22:35

      Да, можно было. Там по горизонтали секунды, вертикальное деление — это 10 секунд. По вертикали % загрузки.
      image


  1. Acuna
    25.05.2016 01:25

    Невероятно! Надо бы им на досуге интро на php.net перевести в качестве благодарности…


  1. keltanas
    25.05.2016 09:01

    Судя по графикам у меня создалось впечатление, что это все равно не мультипоточность, а мультипроцессовость, которые в PHP почему-то постоянно путают.


    1. mnv
      25.05.2016 10:42

      Автор расширения пишет, что именно многопоточность.


      This project provides multi-threading that is compatible with PHP based on Posix Threads.


    1. Fesor
      25.05.2016 15:26

      На самом деле разница между многопроцессностью и многопоточностью проявляется только в общем потреблении ресурсов (потоки меньше жрут, что как бы логично) и в меньшем времени, необходимом на старт. А графики выглядели бы примерно так же. Просто с потоками не нужно париться о организации IPC