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

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

Прелесть 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.

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

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

Начну же я с того, что нужно сделать, чтобы ваш багаж НЕ потеряли:
1. Оторвите с чемодана все бирки и стикеры от предыдущих поездок, даже маленькие со штрих-кодом, которые часто клеят отдельно на сам чемодан - они могут сбить с толку автоматическую систему сканирования и сортировки багажа.
2. Повесьте на чемодан (сумку, коробку, сверток - в общем, все, что вы сдаете в багаж) именную бирку: можно купить заранее многоразовый вариант или взять бумажную бирку на стойке регистрации - обычно все мало-мальски приличные авиакомпании раздают их без ограничений. А у Emirates, например, вообще отличные пластиковые бирки на прочном шнурке, которые могут прослужить очень долго:

Старые параноики могут сделать как я: у меня на чемодане всегда висит многоразовая пластиковая бирка из набора Samsonite с моим постоянным домашним адресом, телефоном и электронной почтой, а когда я лечу куда-то на отдых, то дополнительно вешаю бумажную, на которой указываю даты пребывания на новом месте и все возможные контакты (название и адрес отеля, его местный номер телефона, если есть, ну и свои имя-фамилию, разумеется).
3. На стойке регистрации проследите, чтобы на ваш багаж наклеили ту багажную бирку, которую распечатывает агент по регистрации, - с кодом города, в который вы летите, и номером рейса.
4. Если у вас несколько стыковочных рейсов, скажите об этом агенту по регистрации, и уточните, до какого именно пункта хотите зарегистрировать багаж. В отдельных случаях багаж придется забирать в том или ином аэропорту на пути следования вне зависимости от вашего желания: это касается, например, пересадок между аэропортами ("Орли" и "Шарль-де-Голль" в Париже, "Домодедово"-"Шереметьево"-"Внуково" в Москве), отдельными терминалами (1 и 2 терминалы во Франкфурте) или в первом пункте прилета в США или Мексику - это требование таможни в этих странах: предположим, вы летите Москва-Вашингтон-Феникс, багажная бирка выпускается на все три сегмента до Феникса, но в Вашингтоне багаж нужно будет физически забрать, пройти таможню и снова сдать.Также, если вы сдаете в багаж детскую коляску, которую вам разрешили взять до трапа самолета, или животное, вероятна необходимость забрать его в транзитном пункте. Вообще, в случае сложного маршрута с пересадками лучше заранее уточнить нюансы о передвижениях багажа в колл-центре авиакомпании или, в крайнем случае, на регистрации.
5. Сделайте свой багаж заметным: не всегда в задержке багажа виноваты грузчики или сбои в системе сортировки. Иногда другой рассеянный пассажир, уставший после долгого перелета, возьмет с багажной ленты такой же черный самсонайт или невзрачную спортивную сумку, как и у вас. Поэтому отметьте свой багаж: повесьте на ручку связку ярких ленточек или небольшую мягкую игрушку, наклейте на него крупную наклейку или просто на этапе выбора чемодана отдайте предпочтение необычной расцветке.

Что не стоит сдавать в багаж?
Помните, багаж теряют все авиакомпании и во всех аэропортах. Безусловно, статистика у всех разная, но потерять или задержать багаж могут даже самые надежные авиалинии и даже в самом маленьком аэропорту, где один-единственный грузчик довезет тележку с чемоданами прямо от стойки регистрации к самолету. Поэтому советую всегда брать в ручную кладь:
- важные документы, в том числе те, которые не нужны при перелете (например, в последнюю поездку в Питер мне нужно было поменять права, и я везла с собой в ручной клади свидетельство о браке и всякие карточки из автошколы)
- ключи (в сочетании с биркой с вашим адресом это может быть опасно)
- деньги, драгоценности (без комментариев)
- дорогостоящую хрупкую технику
- лекарства, которые вы регулярно принимаете, в количестве, необходимом для перелета, и с небольшим запасом на случай, если придется искать аналог в чужой стране или городе. Лекарства по рецепту, которые будет невозможно купить в случае потери багажа, брать с собой в объеме, необходимом на всю поездку.
- то, что может понадобиться срочно по приезду (например, зарядное устройство для телефона
- то, что имеет сентиментальную ценность лично для вас: иногда багаж теряется безвозвратно, и если потеря личного дневника разобьет вам сердце, лучше оставьте его дома или возьмите с собой в салон самолета

Поучительная история: во времена моей работы в питерской Люфтганзе, к нам в офис, заламывая руки, прибежала семейная пара из США - у них не прилетел багаж, в котором лежали очень важные документы для судебного процесса по усыновлению, суд был на следующий день. Конечно, в потере багажа виновата авиакомпания, но кому от этого легче? Чтобы избежать такой ситуации, достаточно было просто положить важные бумаги в ручную кладь.

Итак, вы прилетели и не обнаружили свой багаж на багажной ленте. Что делать?
1. Если вы сдавали в багаж что-то, отличное от обычных чемоданов: лыжи, виолончель, плазменную панель во всю стену, детскую коляску, живого мраморного дога, проверьте, нет ли отдельного пункта выдачи т.н. негабаритного багажа или Bulky baggage - багаж, подобный тому, который я описала выше, часто загружается в отдельный отсек и так же отдельно, вручную, выгружается. Если и там ваш багаж не обнаружился
2. Идите на стойку розыска багажа или Lost & Found. Там вам будет необходимо заполнить специальную форму с подробной информацией о своем багаже: маршрут следования, внешний вид, краткий список содержимого, ваши контакты на постоянном месте жительства и временного пребывания. Также в службе розыска багажа вы скорее увидите вот такой baggage chart:

Именно по этой классификации ваш пропавший багаж закодируют и, чтоб вы понимали, вот эти два чемодана будут закодированы одинаково:

Поэтому не стесняйтесь добавлять дополнительные детали в описании и не пропускайте пункт о содержимом. Как правило, при первичном заполнении заявления о задержке багажа (baggage delay report) вам предложат указать несколько предметов содержимого, по которым вашу сумку можно будет идентифицировать, если снаружи на ней не найдется никаких опознавательных знаков и сумку придется вскрывать (если сумку вскроют, внутрь положат уведомление об этом). Плохой пример: футболка / книга / влажные салфетки, хороший пример: ярко-красное бикини / каталог репродукций Малевича / складной утюг. После заполнения заявления сотрудник службы розыска багажа выдаст вам номер формата XXXYY11111, где XXX - код аэропорта прилета, YY - код авиакомпании прилета + 5 цифр порядкового номера заявления: например JFKLH12345, если вы прилетели "Люфтганзой" в аэропорт Кеннеди в Нью-Йорке. Запомните или запишите этот номер - по нему ваше заявление будет проще всего найти при дальнейших обращениях.
По этому же номеру вы можете САМИ проверять статус розыска ( сылка почему-то слетает: если у вас не работает, забейте в гугле World Tracer Online и буквально вторая ссылка - с заголовком Baggage Tracing на сайте worldtracer.aero - то, что вам надо), потому что дозвониться до lost&found часто бывает очень сложно
3. Попробуйте обратиться в офис вашей авиакомпании в аэропорту прилета: иногда (подчеркиваю - ИНОГДА!) в случае, если вы прилетели не домой, а в место временного пребывания (отпуск, командировка), авиакомпания может предоставить набор туалетных принадлежностей (у Lufthansa в него входили безразмерная футболка, зубная щетка и паста, расческа, маленькие упаковки шампуня и геля для душа, пакетик стирального порошка и пр.) или сделать небольшую выплату наличными на мелкие расходы на месте (spot cash payment).

Что будет происходить дальше?
Ваш файл (т.н. AHL) попадет в централизованную систему поиска багажа (World Tracer). В эту же систему поиска попадают все невостребованные предметы багажа, не важно, были ли они найдены без бирки в закоулках багажного дворика или остались на багажной ленте, на каждый из таких предметов тоже заводится файл формата XXXYY11111, только другого подвида - т.н. on-hand report или OHD. В случае совпадения данных из AHL и OHD файлов (фамилия, описание чемодана, маршрут и проч.), обе станции (где заявили о потере багажа и где был найден багаж невостребованный) получат уведомление, ну и дальше вопрос техники: перепроверка и в случае удачи пересылка багажа в нужный город. Разумеется, осуществляется и большое количество ручной работы - обмен сообщениями, отбраковывание похожих, но не тех самых чемоданов, плюс ответы на множественные телефонные звонки - в общем, сотрудникам службы розыска багажа скучать не приходится.
Примерная статистика: более 90% потерянного багажа находится в первые 3 дня поиска, 3% теряются навсегда.
Что можете сделать вы?
1. Если вам придется покупать что-либо из экстренно необходимого по прилету (начиная от зубной щетки и заканчивая деловым костюмом), обязательно сохраняйте чеки для последующей компенсации. Впрочем от дорогих покупок без нужды стоит отказаться, почему - объясню позже.
2. По свежим следам составьте максимально подробный список содержимого, желательно с цветом, брендом и примерной стоимостью каждой вещи, в идеале на английском языке (т.к. иначе переводить этот список для внесения в систему придется сотруднику авиакомпании), свяжитесь с авиакомпанией и отправьте им этот список, его добавят в заявление о розыске багажа. Первые 5 дней розыском багажа занимается аэропорт прилета, дальше розыск переходит в ведение авиакомпании-перевозчика (та авиакомпания, которая указана в номере заявления - помните JFKLH12345?), а по истечении 21 дня можно обращаться за финальной компенсацией.
3. Если по истечении 21 дня с момента составления заявления о потере багажа, он не был найден, обращайтесь в авиакомпанию-перевозчик с требованием компенсации. Если я не ошибаюсь, срок давности - 2 года, т.е. за компенсацией вы можете обратиться в течение двух лет с момента подачи заявления о потере.

Выплата компенсаций.
Для выплаты компенсации вам будет необходимо обратиться в представительство своей авиакомпании с заявлением на выплату, документами, подтверждающими перелет и факт потери багажа (посадочные талоны, багажные бирки, номер заявления о потере багажа, платежные реквизиты). Если не ошибаюсь, в РФ решение о компенсации законодательно должно быть рассмотрено в течение 30 дней. Также вас могут попросить оценить стоимость содержимого и во возможности предоставить чеки на покупку чемодана и вещей в нем (понимаю, что это малореально в большинстве случаев, но это часть процедуры).
Раньше выплаты производились по принципу веса сданного багажа - около 20 долларов за килограмм. Позже систему расчетов поменяли и ограничили ответственность авиакомпаний суммой в 1000 условных единиц (стоимость условной единицы рассчитывается внутри авиакомпании), что на момент моей работы соответствовало примерно 1300 евро. Т.е. даже если вы принесете чек на покупку чемодана Луи Виттон, сшитого из тысячи шкурок боливийского геккона и набитого бриллиантами, больше 1300 евро вам не светит.

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

Похоже, 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 в случаях из реального мира! Это и будет темой нашего следующего поста.

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