В PHP есть потоки?

Я нашел этот пакет PECL, называемый потоками, но пока нет выпуска. И ничего не происходит на сайте PHP.

13 ответов

Решение

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

Из руководства по PHP для расширения pthreads:

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

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

Первый выпуск PHP4, 22 мая 2000 г., PHP был поставлен с поточно-ориентированной архитектурой - способом для выполнения нескольких экземпляров своего интерпретатора в отдельных потоках в многопоточных средах SAPI ( Server API). За последние 13 лет дизайн этой архитектуры поддерживался и совершенствовался: с тех пор он используется в производственных целях на крупнейших мировых веб-сайтах.

Работа с потоками в пользовательской среде никогда не была проблемой для команды PHP, и сегодня она остается таковой. Вы должны понимать, что в мире, где PHP занимается бизнесом, уже существует определенный метод масштабирования - добавьте оборудование. За многие годы существования PHP, аппаратные средства становились все дешевле и дешевле, и это все меньше и меньше беспокоило команду PHP. В то время как это становилось дешевле, это также становилось намного более сильным; сегодня наши мобильные телефоны и планшеты имеют двухъядерную и четырехъядерную архитектуры и достаточное количество оперативной памяти, наши настольные компьютеры и серверы обычно имеют 8 или 16 ядер, 16 и 32 гигабайта оперативной памяти, хотя мы не всегда можем иметь два в рамках бюджета и наличие двух рабочих столов редко бывает полезным для большинства из нас.

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

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

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

Для тех, кто хочет его изучить, pthreads предоставляет API, который позволяет многопоточным PHP -приложениям. Его API в значительной степени находится в стадии разработки и обозначен как бета-уровень стабильности и полноты.

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

pthreads использует потоки Posix (даже в Windows), то, что создает программист, является реальными потоками выполнения, но для того, чтобы эти потоки были полезны, они должны знать PHP - способны выполнять пользовательский код, совместно использовать переменные и предоставлять полезные средства связи (синхронизация). Таким образом, каждый поток создается с экземпляром интерпретатора, но по своей конструкции его интерпретатор изолирован от всех других экземпляров интерпретатора - так же, как и многопоточные среды API сервера. Pthreads пытается ликвидировать разрыв разумным и безопасным способом. Многие из проблем программиста потоков в C просто не связаны с программистом pthreads. По своей конструкции pthreads - это копирование при чтении и копирование при записи (ОЗУ обходится дешево), поэтому никакие два экземпляра никогда не манипулируют одними и теми же физическими данными., но они оба могут влиять на данные в другом потоке. Тот факт, что PHP может использовать функции небезопасных потоков в своем основном программировании, совершенно не имеет значения, пользовательские потоки и его операции полностью безопасны.

Зачем копировать на чтение и копировать на запись:

public function run() {
    ...
    (1) $this->data = $data;
    ...
    (2) $this->other = someOperation($this->data);
    ...
}

(3) echo preg_match($pattern, $replace, $thread->data);

(1) Пока блокировка чтения и записи удерживается в хранилище данных объекта pthreads, данные копируются из исходного местоположения в памяти в хранилище объектов. pthreads не корректирует refcount переменной, Zend может освободить исходные данные, если на них больше нет ссылок.

(2) Аргумент к someOperation ссылается на хранилище объектов, сохраненные исходные данные, которые он сам является копией результата (1), снова копируется для механизма в контейнер zval, в то время как это происходит, блокировка чтения удерживается хранилище объектов, блокировка снята, и двигатель может выполнить функцию. Когда zval создан, он имеет повторный счет 0, что позволяет механизму освободить копию по завершении операции, поскольку никаких других ссылок на него не существует.

(3) Последний аргумент preg_match ссылается на хранилище данных, получается блокировка чтения, набор данных в (1) копируется в zval, снова с refcount 0. Блокировка освобождается, вызов preg_match работает копия данных, которая сама является копией исходных данных.

Что нужно знать:

  • Хеш-таблица хранилища объектов, в которой хранятся данные, является поточно-ориентированной,
    основанный на TsHashTable, поставляемом с PHP, Zend.

  • Хранилище объектов имеет блокировку чтения и записи, для TsHashTable предусмотрена дополнительная блокировка доступа, так что если требуется (и он делает это, var_dump/print_r, прямой доступ к свойствам, как движок PHP хочет на них ссылаться), то pthreads может манипулировать TsHashTable вне определенного API.

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

Это означает:

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

  • Когда происходит чтение, удерживается не только блокировка чтения, но и дополнительная блокировка доступа, и снова таблица блокируется.

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

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

Большинство библиотек и расширений, доступных для PHP, представляют собой тонкие обертки для сторонних разработчиков, функциональность ядра PHP до некоторой степени идентична. pthreads - не тонкая оболочка вокруг ниток Posix; это API потоков, основанный на потоках Posix. Нет смысла в реализации Threads в PHP, который пользователи не понимают или не могут использовать. Нет причины, по которой человек, не имеющий представления о том, что такое мьютекс или не делает его, не должен иметь возможности воспользоваться всем, что у него есть, как с точки зрения навыков, так и ресурсов. Объект функционирует как объект, но там, где в противном случае два контекста столкнулись бы, pthreads обеспечивает стабильность и безопасность.

Любой, кто работал в java, увидит сходство между объектом pthreads и многопоточностью в java. Те же люди, несомненно, увидят ошибку ConcurrentModificationException - так как она звучит как ошибка, возникающая во время выполнения java, если два потока записывают одинаковые физические данные одновременно. Я понимаю, почему он существует, но меня сбивает с толку тот факт, что с такими дешевыми ресурсами, как они есть, в сочетании с тем фактом, что среда выполнения способна обнаруживать параллелизм в точное и единственное время, когда безопасность может быть достигнута для пользователя, который он выбирает для генерировать возможно фатальную ошибку во время выполнения, а не управлять выполнением и доступом к данным.

Pthreads не выдаст таких глупых ошибок, API написан для того, чтобы сделать поток настолько стабильным и совместимым, насколько это возможно, я полагаю.

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

Наконец, из руководства по PHP:

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

Вот пример того, что предложил Wilco:

$cmd = 'nohup nice -n 10 /usr/bin/php -c /path/to/php.ini -f /path/to/php/file.php action=generate var1_id=23 var2_id=35 gen_id=535 > /path/to/log/file.log & echo $!';
$pid = shell_exec($cmd);

В основном это выполняет сценарий PHP в командной строке, но немедленно возвращает PID, а затем запускается в фоновом режиме. (Echo $! Гарантирует, что ничего, кроме PID, не будет возвращено.) Это позволяет вашему PHP-скрипту продолжить или выйти, если хотите. Когда я воспользовался этим, я перенаправил пользователя на другую страницу, где каждые 5–60 секунд делается вызов AJAX, чтобы проверить, запущен ли отчет. (У меня есть таблица для хранения gen_id и пользователя, с которым он связан.) Сценарий проверки выполняет следующее:

exec('ps ' . $pid , $processState);
if (count($processState) < 2) {
     // less than 2 rows in the ps, therefore report is complete
}

Короткий пост по этой технике здесь: http://nsaunders.wordpress.com/2007/01/12/running-a-background-process-in-php/

Вкратце: да, в php есть многопоточность, но вы должны использовать многопроцессорность.

Справочная информация: потоки против процессов

Всегда существует некоторая путаница в отношении различий потоков и процессов, поэтому я кратко опишу оба:

  • Поток - это последовательность команд, которые процессор будет обрабатывать. Единственные данные, из которых он состоит, это счетчик программ. Каждое ядро ​​ЦП будет обрабатывать только один поток за раз, но может переключаться между выполнением различных потоков посредством планирования.
  • Процесс - это набор общих ресурсов. Это означает, что он состоит из части памяти, переменных, экземпляров объектов, файловых дескрипторов, мьютексов, соединений с базой данных и так далее. Каждый процесс также содержит один или несколько потоков. Все потоки одного и того же процесса совместно используют его ресурсы, поэтому вы можете использовать переменную в одном потоке, который вы создали в другом. Если эти потоки являются частями двух разных процессов, они не могут напрямую обращаться к ресурсам друг друга. В этом случае вам необходимо межпроцессное взаимодействие через, например, каналы, файлы, сокеты...

многопроцессорная обработка

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

В php у вас есть два способа создать новый процесс:

пусть ОС сделает это за вас: вы можете указать своей операционной системе создать новый процесс и запустить в нем новый (или тот же) скрипт php.

  • для Linux вы можете использовать следующее или рассмотреть ответ Даррила Хейна:

    $cmd = 'nice php script.php 2>&1 & echo $!';
    pclose(popen($cmd, 'r'));
    
  • для окон вы можете использовать это:

    $cmd = 'start "processname" /MIN /belownormal cmd /c "script.php 2>&1"';
    pclose(popen($cmd, 'r'));
    

Сделайте это самостоятельно с помощью форка: php также предоставляет возможность использовать разветвление через функцию pcntl_fork (). Хороший учебник о том, как это сделать, можно найти здесь, но я настоятельно рекомендую его не использовать, так как форк - это преступление против человечества и особенно против упа.

Многопоточность

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

Стандартный php не обеспечивает многопоточности, но есть (экспериментальное) расширение, которое на самом деле это делает - pthreads. Его документация API даже превратила его в php.net. С его помощью вы можете делать некоторые вещи, как вы можете на реальных языках программирования:-) вот так:

class MyThread extends Thread {
    public function run(){
        //do something time consuming
    }
}

$t = new MyThread();
if($t->start()){
    while($t->isRunning()){
        echo ".";
        usleep(100);
    }
    $t->join();
}

Для linux здесь есть руководство по установке в stackru.

Для окон теперь есть одно:

  • Для начала вам нужна поточная версия php.
  • Вам нужны предварительно скомпилированные версии обоих pthreads и его расширения php. Их можно скачать здесь. Убедитесь, что вы загружаете версию, совместимую с вашей версией php.
  • Скопируйте php_pthreads.dll (из только что загруженного zip-файла) в папку расширения php ([phpDirectory]/ext).
  • Скопируйте pthreadVC2.dll в [phpDirectory] (корневая папка, а не папка расширения).
  • Отредактируйте [phpDirectory]/php.ini и вставьте следующую строку

    extension=php_pthreads.dll
    
  • Протестируйте его с помощью приведенного выше сценария, немного поспав или где-нибудь там, где есть комментарий.

И теперь большое НО: хотя это действительно работает, php изначально не был создан для многопоточности. Существует поточно-ориентированная версия php, и, начиная с v5.4, она почти не содержит ошибок, но использование php в многопоточной среде все еще не рекомендуется в руководстве по php (но, возможно, они просто не обновляли руководство по это пока). Гораздо более серьезная проблема может заключаться в том, что многие распространенные расширения не являются поточно-ориентированными. Таким образом, вы можете получить потоки с этим расширением php, но функции, от которых вы зависите, все еще не являются потокобезопасными, поэтому вы, вероятно, столкнетесь с условиями гонки, взаимными блокировками и т. Д. В коде, который вы не написали сами...

Вы можете использовать pcntl_fork() для достижения чего-то похожего на потоки. Технически это отдельные процессы, поэтому связь между ними не так проста с потоками, и я считаю, что она не будет работать, если PHP вызывается через apache.

Если кому-то все равно, я возродил php_threading (не то же самое, что потоки, но похожи), и у меня он действительно до такой степени, что он работает (отчасти) хорошо!

Страница проекта

Скачать (для Windows PHP 5.3 VC9 TS)

Примеры

ПРОЧТИ МЕНЯ

pcntl_fork() это то, что вы ищете, но его процесс разветвления не потоков. так что у вас возникнет проблема обмена данными. для их решения вы можете использовать функции семафоров phps ( http://www.php.net/manual/de/ref.sem.php) очереди сообщений могут быть немного проще для начала, чем сегменты разделяемой памяти.

В любом случае, стратегия, которую я использую в разрабатываемой веб-платформе, которая загружает ресурсоемкие блоки веб-страницы (возможно, с внешними запросами) параллельно: я делаю очередь заданий, чтобы узнать, каких данных я жду, а затем разветвляюсь от рабочих мест для каждого процесса. После этого они сохраняют свои данные в кэше apc под уникальным ключом, к которому может обращаться родительский процесс. как только все данные есть, они продолжаются. я использую простой usleep() ждать, потому что межпроцессное взаимодействие невозможно в apache (дети потеряют связь с родителями и станут зомби...). так что это подводит меня к последнему: важно убить каждого ребенка самостоятельно! Есть также классы, которые разветвляют процессы, но хранят данные, я их не проверял, но в Zend Framework они есть, и они обычно медленно, но надежно кодируют. Вы можете найти его здесь: http://zendframework.com/manual/1.9/en/zendx.console.process.unix.overview.html Я думаю, что они используют сегменты SHM! И последнее, но не менее важное: на этом Zend-сайте есть ошибка, незначительная ошибка в примере.

while ($process1->isRunning() && $process2->isRunning()) {
    sleep(1);
}
should of course be:
while ($process1->isRunning() || $process2->isRunning()) {
    sleep(1);
}

Существует расширение Threading, которое разрабатывается на основе PThreads и выглядит очень многообещающе на https://github.com/krakjoe/pthreads.

Просто обновление, похоже, что ребята из PHP работают над поддержкой потоков, и теперь они доступны.

Вот ссылка на него: http://php.net/manual/en/book.pthreads.php

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

РЕДАКТИРОВАТЬ: теперь это доступно как библиотека композитора и как часть моей инфраструктуры MVC, Hazaar MVC.

Смотрите: https://git.hazaarlabs.com/hazaar/hazaar-thread

Я знаю, что это старый вопрос, но вы можете посмотреть на http://phpthreadlib.sourceforge.net/

Двунаправленная связь, поддержка Win32 и никаких расширений не требуется.

Когда-нибудь слышал о appserver из техдивизии?

Он написан на php и работает как сервер приложений, управляющий многопоточностью для приложений php с высоким трафиком. Все еще находится в бета-версии, но очень перспективно

Существует довольно малоизвестная и вскоре устаревшая функция, называемая тиками. Единственное, для чего я когда-либо использовал это - позволить скрипту захватить SIGKILL (Ctrl+C) и аккуратно закрыть.

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