Open
Close

Многопоточные вычисления в PHP: pthreads. Что делать, если ваш багаж потеряли? Анемичный discussion thread php

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

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

Начну же я с того, что нужно сделать, чтобы ваш багаж НЕ потеряли:
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 евро вам не светит.

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

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

Хотя 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 мы ориентируемся в каком потоке находимся в данный момент и выполняем необходимые действия.

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

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