Выбор версии PHP для Windows. Многопоточное программирование в PHP с помощью Pthreads Перевод Малоумный discussion thread php

Threaded discussion

A threaded discussion is an electronic discussion (such as one via e-mail , e-mail list , bulletin board , newsgroup , or Internet forum) in which the software aids the user by visually grouping messages. Messages are usually grouped visually in a hierarchy by topic. A set of messages grouped in this way is called a topic thread or simply "thread". A discussion forum, e-mail client or news client is said to have "threaded topics" if it groups messages on the same topic together for easy reading in this manner. Moreover, threaded discussions typically allow users to reply to particular posting within a topic"s thread. As a result, there can be a hierarchy of discussions within the thread topic. Various types of software may allow this hierarchy to be displayed in what"s called Threaded Mode. (The alternative being Linear Mode, which typically shows all of the posts in date order, regardless of who may have specifically replied to whom.)

Advantages

The advantage of hierarchically threaded views is that they allow the reader to appreciate quickly the overall structure of a conversation: specifically who is replying to whom. As such it is most useful in situation with extended conversations or debates, such as newsgroups: indeed, for really complex debate, it quickly becomes impossible to follow the argument without some sort of hierarchical threading system in place.

Another benefit is in the more subtle appreciation of community in hierarchically threaded systems. As responses have to be made to specific posts, they are also made to specific individuals. Threaded conversations therefore tend to focus the writer on the specific views and personality of the individual being responded to. This occurs less in fora where the latest comment is just inserted into the general pool.

Disadvantages

A disadvantage of hierarchical threading over flat threading is an increased level of complication, and such a view therefore requires an increased level of comfort and sophistication on the part of its users. It is therefore not surprising that its takeup has been heaviest in some of the oldest and/or most sophisticated of online communities, such as Usenet , CIX or Slashdot . Web chat and comment systems are, by comparison, younger and open to a wider audience, and as such hierarchical threading is only recently becoming commonplace in such arenas.

Imposing a tree hierarchy also tends to fragment discussion within a topic: it no longer becomes possible to post a message responding to or summarising several different previous posts. Instead, every previous post must be responded to individually. It is arguable that this leads to a more confrontational debating style in fora that use hierarchical threading. However, true though that may be, if a direct threaded reply is no longer possible due to volume of replies to the desired post, users are now often using quotes by the person they are responding to in order to keep the conversation on track and flowing smoothly. This is recommended by most message board communities in the event that the threading has reached its otherwise comprehensive limit.

Open thread

An open thread refers to a blog post where readers may comment and discuss any topic that they choose. They are usually more useful on popular blogs with large amounts of traffic ; they are often used when the author of the blog has no subject matter to post on or when there is a lull in posting.

Open threads are also used to break up the monotony of posts on the main pages of blogs. Comments may build up on content-oriented posts; therefore, authors use the open threads so page load times won"t be slowed down.

Examples

* Yahoo! Groups [ http://groups.yahoo.com/ ] , MSN Groups [ http://groups.msn.com/ ] and Slashdot [ http://www.slashdot.com/ ] all offer web-based forums that feature threaded discussions.

See also

* Scholarly Skywriting
* List of blogging terms

References

*Dartmouth. (2003). [ http://www.dartmouth.edu/~webteach/articles/discussion.html "Taking discussion online" ]
*Wolsey, T. DeVere, [ http://www.readingonline.org/articles/art_index.asp?HREF=wolsey/index.html "Literature discussion in cyberspace: Young adolescents using threaded discussion groups to talk about books ] . "Reading Online", 7(4), January/February 2004. Retrieved December 30, 2007

Wikimedia Foundation . 2010 .

  • Leon Powe
  • Barh Azoum

Look at other dictionaries:

    Internet forum - The phpBB Internet Forum software package, one of the most popular forum packages … Wikipedia

    History of virtual learning environments 1990s - In the history of virtual learning environments, the 1990s was a time of growth, primarily due to advent of the affordable computer and of the Internet.1990s1990* Formal Systems Inc. of Princeton, NJ, USA introduces a DOS based Assessment… … Wikipedia

    CoFFEE - Collaborative Face to Face Educational Environment Developer(s) LEAD consortium Stable release 5.0 / June 2010 Operating system Cross platform … Wikipedia

    Conversation threading - is a feature used by many email clients, bulletin boards, newsgroups, or Internet forums in which the software aids the user by visually grouping messages. Messages are usually grouped visually in a hierarchy by topic. A set of messages grouped… … Wikipedia

    Slashdot - Screenshot of the Slashdot.org main page URL slashdot.org Slogan News for nerds. Stuff that matters … Wikipedia

    MediaWiki - namespace redirects here. For help regarding the MediaWiki namespace on Wikipedia, see Help:MediaWiki namespace. For general information about Wikipedia namespaces, see Wikipedia:Namespace. Talk page and MediaWiki talk page redirect here. For… … Wikipedia

    Computer-mediated communication - For other uses, see CMC (disambiguation). Computer mediated communication (CMC) is defined as any communicative transaction that occurs through the use of two or more networked computers. While the term has traditionally referred to those… … Wikipedia

    Comparison of wiki software - The following tables compare general and technical information for a number of wiki software packages. Contents 1 General information 2 Target audience 3 Features 1 4 Features 2 … Wikipedia

    Scholarly Skywriting - is a term coined by cognitive scientist Stevan Harnad describing the combination of multiple email and a topic threaded web archive such as a newsgroup, electronic mailing list, hypermail, netnews or Internet forum, linked and sortable by date,… … Wikipedia

    Collaborative decision-making software - Collaborative decision making (CDM) software is a software application or module that coordinates the functions and features required to arrive at timely collective decisions, enabling all relevant stakeholders to participate in the process. The… … Wikipedia

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

Прелесть open-source кода в его открытости:)) Т.е. при наличии ума/времени/желания можно разобраться, как именно работает программа. Обратная сторона такого кода - сложность в получении нужных скомпилированных пакетов. Например, PHP можно скачать в виде исходников для Nix-систем с последующей компиляцией/сборкой. Для Windows все уже собрано, но готовых бинарных пакетов много! Варианты с "thread safe/non thread safe ", VC6/VC9 и разные версии самого PHP. Статья создана для прояснения ситуации. В основе - разные источники, частично - перевод с английского. Все для того, чтоб в следующий раз мне опять не разбираться - "че к чему!?".

Нужная версия PHP зависит от версии веб-сервера, на котором он будет использоваться. Например, Apache 1.3.x работает с РНР версии 3.0.х, Apache 2.х работает с РНР версии 4.0 и выше. Но это не такая уж проблема, ориентируйтесь на более новые стабильные релизы и то, что стоит у хостера.

Что за приписки VC6, VC9, VC11 ? Исходники PHP под Windows компилируются в Visual Studio. VC9 получается при компиляции в VS 2008, VC11 - Visual Studio 2012. Соответственно, чтобы все это дело у вас работало, на компе должны быть установлены библиотеки Visual C++ Redistributable for Visual Studio соответствующего года. Некоторые разъяснения по этому поводу .

Кроме того, если web-сервером у вас будет старенький Apache с сайта apache.org, то нужно качать VC6 версии PHP, для компиляции которых использовался Visual Studio 6. Если же PHP будет работать для IIS или в связке с более новым Apache , то можно собрать что-нибудь посовременнее;)

Для меня главным ступором в выборе служит хостер. Сейчас есть стабильная версия PHP 5.5.4, а у него до сих пор 5.2.17!

Теперь самая интересная часть: "thread safe or non thread safe? "
Вольный перевод статьи (Dominic Ryan, 27.09.2007)

Я настолько ломанного английского еще не видел:((Хотел по-быстрому перевести статью, но с трудом понимаю, что автор понаписал. Постоянные переходы между "what-is-that" и сложно-составные предложения вообще выносят мОСк. Перевод на русский так же осложняется тем, что у меня не хватает знаний и фантазии как правильно по-русски должно называться то, что обычно пишется только на английском %) Например техническое понятие "multi proccess architecture" я ни разу не видел на русском, а мой перл "потоко-небезопасные" вообще под вопросом здравого смысла. Вообщем, что получилось, то привожу.

Разница между thread safe и non thread safe бинарными пакетами PHP

С тех пор, когда PHP впервые появился под Windows 20 октября 2000 года в версии PHP 3.0.17, его бинарные пакеты всегда были собраны как потоко-безопасные (thread safe, TS) . Основание следующее: Windows использует мульти-поточную архитектуру работы, а Nix-системы поддерживают мульти-процессовую архитектуру. Если PHP был скомпилирован как мульти-процессовое CGI-приложение вместо мульти-поточного, то его использование в качестве CGI-модуля под Windows на сервере IIS приводит к сильным тормозам и загрузке процессора. С другой стороны, можно подключить PHP на IIS, как ISAPI-модуль (требуется мульти-поточная сборка - прим. переводчика). Тогда возникает другая проблема: некоторые популярные расширения PHP разработаны с ориентиром на Unix/Linux, т.е. с мульти-процессовой архитектурой, что приводит к краху PHP, подключенному на IIS в качестве ISAPI-модуля. Т.о. создание CGI - наиболее стабильная среда для PHP на IIS с основным недостатком, что это ужасно медленно. Приходится загружать и выгружать всю среду PHP из памяти каждый раз, когда есть запрос.

В то время было несколько вариантов для увеличения производительности PHP на IIS. Первый - использовать кеширование опкода программами типа eAccelerator, которые сохраняют PHP-скрипты в частично скомпилированном состоянии на диске и/или в памяти. Такой подход значительно сокращает время выполнения скрипта. Другой вариант заключался в настройке IIS на использование PHP в режиме FastCGI . При этом PHP-процесс после отработки не закрывался, а получал новое задание с очередным php-запросом. К тому же можно было запустить несколько PHP-процессов одновременно, ощутимо ускоряя обработку запросов, что являлось бонусом CGI-режима PHP. При этом могли быть незначительные проблемы с совместимостью PHP-расширений. Это по-прежнему самый быстрый способ использования PHP, и именно на задание такой конфигурации IIS настроен установщик "IIS Aid PHP Installer".

Бинарники, собранные в потоко-небезопасном режиме (non thread safe, NTS) , позволяют сконфигурировать IIS (и другие веб-сервера под Windows) на использование PHP, как стандартный CGI-интерфейс с сильным приростом производительности, т.к. в этом случае (в такой сборке) PHP-процессу не нужно дожидаться синхронизации нитей. При сравнении работы "thread safe" и "non thread safe" бинарных пакетов PHP на IIS в качестве стандартного CGI-интерфейса прирост быстродействия составляет до 40%, но это все равно не так шустро как использование опкода в FastCGI методе. А самый большой косяк в том, что нельзя стабильно использовать потоко-небезопасные бинарники вместе с потоко-безопасными. Это значит, что вы не можете использовать системы кеширования опкода типа eAccelerator в среде PHP, созданной потоко-небезопасными бинарными пакетами (утверждение, верное на момент написания статьи).

Если потоко-небезопасный PHP нельзя сконфигурировать до такой же скорости, что и потоко-безопасную среду, то зачем он нужен в такой сборке? Возвращаемся к FastCGI и разработкам Microsoft в этой области за последние несколько лет. Кодеры мелкомягких создали свой вариант FastCGI, который позволяет конфигурировать потоко-небезопасные бинарники PHP в режиме FastCGI, что доводит производительность до скорости света:)

Из статьи я сделал вывод, что тормоза наблюдаются только при использовании с веб-сервером IIS. В любом случае, тупняков под Windows+Apache я не видел. В ней же сказано, что можно разогнать NTS-сборку на любом веб-сервере, но я не представляю себе такой конфиг Apache.

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

В этой статье мы взглянем на то, как многопоточность может быть достигнута в PHP с помощью расширения pthreads . Для этого потребуется установленная ZTS (Zend Thread Safety) версия PHP 7.x, вместе с установленным расширением pthreads v3. (На момент написания статьи, в PHP 7.1 пользователям нужно будет установить из ветки master в репозитории pthreads - см. стороннее расширение.)

Небольшое уточнение: pthreads v2 предназначена для PHP 5.x и более не поддерживается, pthreads v3 - для PHP 7.х и активно развивается.

После такого отступления, давайте сразу перейдём к делу!

Обработка разовых задач

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

Например:

$task = new class extends Thread { private $response; public function run() { $content = file_get_contents("http://google.com"); preg_match("~(.+)~", $content, $matches); $this->response = $matches; } }; $task->start() && $task->join(); var_dump($task->response); // string(6) "Google"

Здесь метод run — это наша обработка, которая будет выполняться внутри нового потока. При вызове Thread::start , порождается новый поток и вызывается метод run . Затем мы присоединяем порождённый поток обратно в основной поток, вызвав Thread::join , который будет заблокирован до тех пор, пока порождённый поток не завершит своё выполнение. Это гарантирует, что задача завершит выполнение, прежде чем мы попытаемся вывести результат (который хранится в $task->response).

Возможно, не желательно загрязнять класс дополнительной ответственностью, связанной с логикой потока (в том числе обязанность определения метода run). Мы можем выделить такие классы, унаследовав их от класса Threaded . Тогда они могут быть запущены внутри другого потока:

Class Task extends Threaded { public $response; public function someWork() { $content = file_get_contents("http://google.com"); preg_match("~ (.+) ~", $content, $matches); $this->response = $matches; } } $task = new Task; $thread = new class($task) extends Thread { private $task; public function __construct(Threaded $task) { $this->task = $task; } public function run() { $this->task->someWork(); } }; $thread->start() && $thread->join(); var_dump($task->response);

Любой класс, который должен быть запущен в отдельном потоке, должен наследоваться от класса Threaded . Это потому что он предоставляет необходимые возможности для выполнения обработки в разных потоках, а также неявную безопасность и полезные интерфейсы (такие, как синхронизация ресурсов).

Давайте взглянем на иерархию классов, предлагаемую расширением pthreads:

Threaded (implements Traversable, Collectable) Thread Worker Volatile Pool

Мы уже рассмотрели и узнали основы классов Thread и Threaded , теперь давайте взглянем на остальные три (Worker , Volatile , и Pool).

Переиспользование потоков

Запуск нового потока для каждой задачи, которую нужно распараллелить, достаточно затратно. Это потому что архитектура "ничего-общего" должна быть реализована в pthreads, чтобы добиться многопоточности внутри PHP. Что означает, что весь контекст выполнения текущего экземпляра интерпретатора PHP (в том числе и каждый класс, интерфейс, трейт и функция) должна быть скопирована для каждого созданного потока. Поскольку это влечет за собой заметное влияние на производительность, поток всегда должен быть повторно использован, когда это возможно. Потоки могут быть переиспользованы двумя способами: с помощью Worker -ов или с помощью Pool -ов.

Класс Worker используется для выполнения ряда задач синхронно внутри другого потока. Это делается путем создания нового экземпляра Worker -а (который создает новый поток), а затем внесением задач в стек этого отдельного потока (с помощью Worker::stack).

Вот небольшой пример:

Class Task extends Threaded { private $value; public function __construct(int $i) { $this->value = $i; } public function run() { usleep(250000); echo "Task: {$this->value}\n"; } } $worker = new Worker(); $worker->start(); for ($i = 0; $i stack(new Task($i)); } while ($worker->collect()); $worker->shutdown();

В вышеприведённом примере в стек заносится 15 задач для нового объекта $worker через метод Worker::stack , а затем они обрабатываются в порядке их внесения. Метод Worker::collect , как показано выше, используется для очистки задач, как только они закончат выполнение. С его помощью внутри цикла while, мы блокируем основной поток, пока не будут завершены все задачи из стека и пока они не будут очищены — до того как мы вызовем Worker::shutdown . Завершение worker -а досрочно (т. е. пока есть еще задачи, которые должны быть выполнены) будет по-прежнему блокировать основной поток до тех пор, пока все задачи не завершат своё выполнение, просто задачи не будут почищены сборщиком мусора (что влечёт за собой утечки памяти).

Класс Worker предоставляет несколько других методов, относящихся к его стеку задач, включая Worker::unstack для удаления последней внесённой задачи и Worker::getStacked для получения количества задач в стеке выполнения. Стек worker -а содержит только задачи, которые должны быть выполнены. Как только задача из стека была выполнена, она удаляется и размещается в отдельном (внутреннем) стеке для сборки мусора (с помощью метода Worker::collect).

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

Давайте адаптируем приведенный выше пример для использования пула worker -ов:

Class Task extends Threaded { private $value; public function __construct(int $i) { $this->value = $i; } public function run() { usleep(250000); echo "Task: {$this->value}\n"; } } $pool = new Pool(4); for ($i = 0; $i submit(new Task($i)); } while ($pool->collect()); $pool->shutdown();

Есть несколько заметных различий при использовании пула, в отличие от воркера. Во-первых, пул не требует запуска вручную, он приступает к выполнению задач, как только они становятся доступными. Во-вторых, мы отправляем задачи в пул, а не укладываем их в стек . Кроме того, класс Pool не наследуется от Threaded , и поэтому он не может быть передан в другие потоки (в отличие от Worker).

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

pthreads и (не)изменяемость

Последний класс, которого мы коснёмся, - Volatile , - новое дополнение к pthreads v3. Понятие неизменяемости стало важной концепцией в pthreads, так как без неё производительность существенно снижается. Поэтому по умолчанию, свойства Threaded -классов, которые сами являются Threaded -объектами, сейчас являются неизменными, и поэтому они не могут быть перезаписаны после их первоначального присвоения. Явная изменяемость для таких свойств сейчас пока предпочтительна, и все еще может быть достигнута с помощью нового класса Volatile .

Давайте взглянем на пример, который продемонстрирует новые ограничения неизменяемости:

Class Task extends Threaded // a Threaded class { public function __construct() { $this->data = new Threaded(); // $this->data is not overwritable, since it is a Threaded property of a Threaded class } } $task = new class(new Task()) extends Thread { // a Threaded class, since Thread extends Threaded public function __construct($tm) { $this->threadedMember = $tm; var_dump($this->threadedMember->data); // object(Threaded)#3 (0) {} $this->threadedMember = new StdClass(); // invalid, since the property is a Threaded member of a Threaded class } };

Threaded -свойства у классов Volatile , с другой стороны, изменяемы:

Class Task extends Volatile { public function __construct() { $this->data = new Threaded(); $this->data = new StdClass(); // valid, since we are in a volatile class } } $task = new class(new Task()) extends Thread { public function __construct($vm) { $this->volatileMember = $vm; var_dump($this->volatileMember->data); // object(stdClass)#4 (0) {} // still invalid, since Volatile extends Threaded, so the property is still a Threaded member of a Threaded class $this->volatileMember = new StdClass(); } };

Мы видим, что класс Volatile переопределяет неизменяемость, навязанную родительским классом Threaded , чтобы предоставить возможность изменять Threaded -свойства (а также unset() -ить).

Есть ещё один предмет обсуждения чтобы раскрыть тему изменяемости и класса Volatile - массивы. В pthreads массивы автоматически приводятся к Volatile -объектам при присвоении к свойству класса Threaded . Это потому что просто небезопасно манипулировать массивом из нескольких контекстов PHP.

Давайте снова взглянем на пример, чтобы лучше понимать некоторые вещи:

$array = ; $task = new class($array) extends Thread { private $data; public function __construct(array $array) { $this->data = $array; } public function run() { $this->data = 4; $this->data = 5; print_r($this->data); } }; $task->start() && $task->join(); /* Вывод: Volatile Object ( => 1 => 2 => 3 => 4 => 5) */

Мы видим, что Volatile -объекты могут быть обработаны так, как если бы они были массивами, т. к. они поддерживают операции с массивами, такие как (как показано выше) оператор подмножеств (). Однако, классы Volatile не поддерживают базовые функции с массивами, такие как array_pop и array_shift . Вместо этого, класс Threaded предоставляет нам подобные операции как встроенные методы.

В качестве демонстрации:

$data = new class extends Volatile { public $a = 1; public $b = 2; public $c = 3; }; var_dump($data); var_dump($data->pop()); var_dump($data->shift()); var_dump($data); /* Вывод: object(class@anonymous)#1 (3) { ["a"]=> int(1) ["b"]=> int(2) ["c"]=> int(3) } int(3) int(1) object(class@anonymous)#1 (1) { ["b"]=> int(2) } */

Другие поддерживаемые операции включают в себя Threaded::chunk и Threaded::merge .

Синхронизация

В последнем разделе этой статьи мы рассмотрим синхронизацию в pthreads. Синхронизация — это метод, позволяющий контролировать доступ к общим ресурсам.

Для примера, давайте реализуем простейший счетчик:

$counter = new class extends Thread { public $i = 0; public function run() { for ($i = 0; $i i; } } }; $counter->start(); for ($i = 0; $i i; } $counter->join(); var_dump($counter->i); // выведет число от 10 до 20

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

Давайте исправим это так, что мы получим правильный вывод 20 , путем добавления синхронизации:

$counter = new class extends Thread { public $i = 0; public function run() { $this->synchronized(function () { for ($i = 0; $i i; } }); } }; $counter->start(); $counter->synchronized(function ($counter) { for ($i = 0; $i i; } }, $counter); $counter->join(); var_dump($counter->i); // int(20)

Синхронизированные блоки кода могут также взаимодействовать друг с другом, используя методы Threaded::wait и Threaded::notify (или Threaded::notifyAll).

Вот поочерёдный инкремент в двух синхронизированных циклах while:

$counter = new class extends Thread { public $cond = 1; public function run() { $this->synchronized(function () { for ($i = 0; $i notify(); if ($this->cond === 1) { $this->cond = 2; $this->wait(); } } }); } }; $counter->start(); $counter->synchronized(function ($counter) { if ($counter->cond !== 2) { $counter->wait(); // wait for the other to start first } for ($i = 10; $i notify(); if ($counter->cond === 2) { $counter->cond = 1; $counter->wait(); } } }, $counter); $counter->join(); /* Вывод: int(0) int(10) int(1) int(11) int(2) int(12) int(3) int(13) int(4) int(14) int(5) int(15) int(6) int(16) int(7) int(17) int(8) int(18) int(9) int(19) */

Вы можете заметить дополнительные условия, которые были размещены вокруг обращения к Threaded::wait . Эти условия имеют решающее значение, поскольку они позволяют синхронизированному колбэку возобновить работу, когда он получил уведомление и указанное условие равно true . Это важно, потому что уведомления могут поступать из других мест, кроме как при вызове Threaded::notify . Таким образом, если вызовы метода Threaded::wait не были заключены в условиях, мы будем выполнять ложные вызовы пробуждения , которые приведут к непредсказуемому поведению кода.

Заключение

Мы рассмотрели пять классов пакета pthreads (Threaded , Thread , Worker , Volatile и Pool), а также как каждый из классов используется. А ещё мы взглянули на новую концепцию неизменяемости в pthreads, сделали краткий обзор поддерживаемых возможностей синхронизации. С этими основами, мы можем теперь приступить к рассмотрению применения pthreads в случаях из реального мира! Это и будет темой нашего следующего поста.

Если вам интересен перевод следующего поста, дайте знать: комментируйте в соц. сетях, плюсуйте и делитесь постом с коллегами и друзьями.

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

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

Хотя PHP и не поддерживает многопоточность, есть несколько методов её эмуляции, о них и пойдет речь ниже.

1. Запуск нескольких копий скрипта - по копии для операции

//woman.php if (!isset($_GET["thread"])) { system("wget http://localhost/woman.php?thread=make_me_happy"); system("wget http://localhost/woman.php?thread=make_me_rich"); } elseif ($_GET["thread"] == "make_me_happy") { make_her_happy(); } elseif ($_GET["thread"] == "make_me_rich") { find_another_one(); }

Когда мы выполняем этот скрипт без параметров, он автоматически запускает две копии себя, с идентификаторами операций ("thread=make_me_happy" и "thread=make_me_rich"), которые инициируют выполнение необходимых функций.

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

2. Путь джедая - использование расширения PCNTL

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

$pid = pcntl_fork(); if ($pid == 0) { make_her_happy(); } elseif ($pid > 0) { $pid2 = pcntl_fork(); if ($pid2 == 0) { find_another_one(); } }

Выглядит довольно запутанно, пройдемся построчно.

В первой строке мы "форкаем" текущий процесс (форк - копирование процесса из сохранением значений всех переменных), разделяя на два процесса (текущий и дочерний) выполняющихся параллельно.

Чтобы понять, где мы находимся в данный момент, в дочернем или материнском процессе, функция pcntl_fork возвращает 0 для дочернего и идентификатор процесса для материнского. Поэтому, во второй строке, мы смотрим на $pid, если он равен нулю, значит мы в дочернем процессе - выполняем функцию, в противном случае, мы находимся в материнском (строка 4), тогда создаем еще один процесс и аналогично выполняем задачу.

Процесс выполнения скрипта:

Таким образом скрипт создает еще 2 дочерних процесса, которые являются его копиями, содержат те же переменные с аналогичными значениями. А при помощи идентификатора возвращаемого функцией pcntl_fork мы ориентируемся в каком потоке находимся в данный момент и выполняем необходимые действия.

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



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



Что такое pthreads

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

Настройка PHP

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


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


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


Резюме

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

Теги: Добавить метки