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

115

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

  • 0
    Кто-нибудь знает, будет ли это ( pcntl_fork() ) работать, если pcntl_fork() из Apache?
  • 0
    Это невероятно старый, но у меня есть ответ, который на самом деле обеспечивает многопоточность в php (см. Ссылки ниже).
Показать ещё 4 комментария
Теги:
multithreading
command-line-interface

13 ответов

38
Лучший ответ

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

  • 1
    Это то, о чем я думал. Я видел множество старых сообщений, говорящих «нет» и ничего на php.net, так что это была моя мысль. Спасибо за подтверждение.
  • 2
    Да, этот пакет PECL - своего рода поддразнивание - я тоже наткнулся на него, но из этого ничего не вышло.
Показать ещё 1 комментарий
163

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

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

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

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

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

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

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

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

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

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

pthreads использует Posix Threads (даже в 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 не корректирует пересчет переменной, Zend может освобождать исходные данные, если дальнейших ссылок на нее нет.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • 1
    Не могли бы вы уточнить это: «... так что никакие два экземпляра никогда не манипулируют одними и теми же физическими данными, но они оба могут влиять на данные в другом потоке ...» В моем понимании, если один поток может влиять на данные в другом, этот другой поток имеет позаботиться о синхронизации. Если этого не произойдет (не-поточно-ориентированная библиотека), вы обречены - независимо от того, используете ли вы копирование при записи или делаете это напрямую. Что я не получу здесь?
  • 0
    Чтобы я мог правильно ответить, я обновил оригинальный вопрос.
Показать ещё 22 комментария
50

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

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

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/

  • 0
    У меня небольшая проблема, как вы проверяете состояние фонового процесса? ты можешь просветить меня
25

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

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

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

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

Multiprocessing

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

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

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

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

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

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

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

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

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

Стандартный 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 существует руководство по установке https://stackoverflow.com/questions/14081444/how-to-use-pthreads-php-extension-in-ubuntu

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

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

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

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

  • 5
    Это ужасно неправильно, статья, на которую вы ссылались, написана в 2008 году. Если бы PHP не был безопасен для потоков в ядре, он бы не имел потоковых модулей SAPI.
  • 1
    @Joe: Хорошо, я изменил его в ядре, потокобезопасен, но многие расширения не являются.
Показать ещё 4 комментария
17

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

  • 4
    Я успешно использую pcntl_fork для распараллеливания довольно масштабной задачи импорта данных. Прекрасно работает, и у меня это работало примерно через час. Есть немного кривой обучения, но как только вы поймете, что происходит, это довольно просто.
  • 0
    Фрэнк, это с CLI php или apache PHP?
Показать ещё 4 комментария
12

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

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

Загрузить (для Windows PHP 5.3 VC9 TS)

Примеры

README

7

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

В любом случае, стратегия, которую я использую в веб-среде, которую я разрабатываю, которая загружает ресурсоемкие блоки веб-страницы (возможно, с внешними запросами) параллельно: Я делаю очередь заданий, чтобы узнать, какие данные я жду, а затем я вилка от рабочих мест для каждого процесса. после их хранения они хранят свои данные в кэше apc под уникальным ключом, к которому может обращаться родительский процесс. после того, как все данные будут продолжены. Я использую простой usleep(), чтобы ждать, потому что связь между процессами невозможна в apache (дети потеряют связь со своими родителями и станут зомби...). поэтому это подводит меня к последнему: его важно убить каждого ребенка! есть также классы, которые обрабатывают процессы fork, но сохраняют данные, я не рассматривал их, но у 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);
}
6

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

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

6

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

5

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

EDIT: теперь он доступен как библиотека композитора и как часть моей MVC-структуры, Hazaar MVC.

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

  • 0
    Что, если, следуя вашему примеру, программа в файле file.php, скажем, например, проверяет наличие списка Uris веб-сайта 10k, а затем сохраняет результат в CSV-файле ... Будет ли запись этого файла проблема?
  • 0
    Подпроцесс будет запущен от имени того же пользователя, что и веб-сервер / родительский скрипт. Так что при написании файлов вы будете иметь те же соображения относительно разрешений, что и обычно. Если у вас есть проблемы с записью файлов, попробуйте записать в / tmp, и когда это работает, перейдите оттуда.
Показать ещё 2 комментария
2

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

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

1

Когда-либо слышал о appserver от techdivision?

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

-4

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

  • 3
    Тики не выполняются параллельно. По сути, после каждого оператора ваша тиковая функция запускается. Пока ваша функция галочки запущена, основной код не работает.
  • 1
    галочки нужны только для обработчика сигнала ().

Ещё вопросы

Сообщество Overcoder
Наверх
Меню