9

Что такое потокобезопасность и непотокобезопасность в PHP?

12

Я увидел разные бинарные файлы для PHP, такие как "non-threaded" и "thread safe".

Что это означает?

В чем разница между этими пакетами?

3 ответ(ов)

3

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

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

Версию с поддержкой потоков нужно выбирать, если вы устанавливаете PHP в качестве модуля Apache в рабочей модели (worker MPM) или в другом окружении, где несколько потоков PHP выполняются одновременно. Проще говоря, любой CGI/FastCGI билд PHP не требует поддержки потоков.

0

Использование Apache MPM prefork с mod_php связано с тем, что это легко настраивается и устанавливается. Однако с точки зрения производительности такое решение довольно неэффективно. Мой предпочтительный вариант стека – FastCGI/PHP-FPM. Это позволяет использовать гораздо более быстрый MPM Worker. PHP остается не потоковым, в то время как Apache обслуживается потоками (как и должно быть).

Таким образом, в правильной последовательности снизу вверх получится следующее:

  • Linux
  • Apache + MPM Worker + ModFastCGI (НЕ FCGI) | (или) | Cherokee | (или) | Nginx
  • PHP-FPM + APC

Следует отметить, что ModFCGI неправильно поддерживает PHP-FPM или любые внешние FastCGI приложения. Он поддерживает только FastCGI скрипты без управления процессами. PHP-FPM – это менеджер процессов FastCGI для PHP.

0

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

Во-первых, PHP компилируется как встроенная библиотека, такая как libphp.so на *NIX и php.dll на Windows. Эта библиотека может быть встроена в любое приложение на C/CPP, но, очевидно, в первую очередь используется на веб-серверах. В своей основе PHP запускается в два основных этапа: фазе инициализации модуля и фазе инициализации запроса. Фаза инициализации модуля инициализирует ядро PHP и все расширения, тогда как фаза инициализации запроса инициализирует пользовательское пространство PHP — как нативные функции пользовательского пространства, так и сам PHP-код.

Библиотека PHP настроена таким образом, что фаза модуля вызывается только один раз, а фаза запроса должна быть переинициализирована для каждого HTTP-запроса. Обратите внимание, что CLI ссылается на ту же библиотеку, что и mod_php и т. д., и все равно должна проходить через эти фазы, даже если она может не использоваться в контексте обработки HTTP-запросов. Также важно заметить, что PHP не предназначен в буквальном смысле для обработки HTTP-запросов — точнее, он предназначен для обработки событий CGI. И снова, это не только php-cgi, но и все SAPI/приложения, включая php-fpm, mod_php, CLI и даже чрезвычайно редкие настольные приложения на PHP.

Веб-серверы (или, что более типично, SAPIs), которые ссылаются на libphp, обычно следуют одному из четырех паттернов:

  • создание совершенно нового экземпляра PHP на каждый запрос (старый паттерн CGI, не распространен и явно не рекомендуется)
  • создание одного экземпляра PHP, но прохождение обеих инициализационных фаз (вместе) в отдельных форкнутых дочерних процессах
  • создание одного экземпляра PHP, выполнение инициализации модуля один раз в родительском процессе перед форком, а затем индивидуальная фаза запроса после форка для каждого HTTP-запроса

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

Четвертый пример относится к потоковым реализациям:

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

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

Независимо от того, как потоковые реализации используют libphp, если фаза модуля инициализируется в одном потоке, а фазы запроса вызываются в разных потоках (что и является тем, для чего PHP был разработан), это требует значительного объема синхронизации не только внутри ядра PHP, но и внутри всех нативных расширений PHP. Обратите внимание, что на данном этапе это не просто вопрос «запроса», а синхронизации, которая вызывается для каждого OPCODE PHP, который зависит от любого вида ресурсов внутри ядра PHP (или любого расширения PHP), существующих в другом потоке по сравнению с пользовательским пространством PHP.

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

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

Если это не очевидно на данный момент, вопрос потокобезопасности против непотокобезопасности — это не просто вопрос того, как PHP работает внутри как «обработчик запросов» в реализации SAPI, но и вопрос о том, как PHP работает внутри как встроенная виртуальная машина для языка программирования.

Это все становится возможным благодаря TSRM, или менеджеру потокобезопасных ресурсов, который хорошо сделан и обрабатывает очень большой объем синхронизации с малозаметными накладными расходами, но они определенно присутствуют и будут расти не только в зависимости от того, сколько запросов в секунду сервер должен обрабатывать (что является решающим фактором для того, сколько потоков требуется SAPI), но и от того, сколько кода PHP используется на запрос (или под время выполнения). Другими словами, крупные нагроможденные фреймворки могут значительно повлиять на накладные расходы TSRM. Это не затрагивает общую производительность PHP и требования к ресурсам в рамках потокобезопасного PHP, а лишь дополнительные накладные расходы самого TSRM в рамках потокобезопасного PHP.

Таким образом, предварительно скомпилированный PHP распространяется в двух вариантах: один, в котором TSRM активен в libphp (потокобезопасный), и один, в котором libphp не использует никаких функций TSRM (непотокобезопасный), а значит, не имеет накладных расходов от TSRM.

Также обратите внимание, что флаг, используемый для компиляции PHP с TSRM (--enable-maintainer-zts или --with-zts в более поздних версиях PHP), вызывает у phpize расширение этого на компиляцию расширений и то, как они инициализируют свои собственные библиотеки (libssl, libzip, libcurl и т. д.), которые часто могут иметь свои собственные способы компиляции для потокобезопасных и непотокобезопасных реализаций, например свои собственные механизмы синхронизации вне TSRM и PHP в целом. Хотя это не совсем связано с PHP, в конечном итоге это все равно будет влиять на производительность PHP вне TSRM (то есть в дополнение к TSRM). Таким образом, расширения PHP (и их зависимости, а также внешние библиотеки, на которые ссылаются PHP или зависимости расширений) часто будут иметь разные атрибуты в потокобезопасных дистрибутивах PHP.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь