Attention! This article is hopelessly outdated or is now assessed by the author as having no informational benefit.

Lovely open-source code in its openness :)) That is. if you have the intelligence/time/desire, you can figure out exactly how the program works. The downside of such code is the difficulty in getting the necessary compiled packages. For example, PHP can be downloaded as sources for Nix systems with subsequent compilation/assembly. Everything is already assembled for Windows, but there are a lot of ready-made binary packages! Options with " thread safe/non thread safe", VC6/VC9 And different versions PHP itself. The article was created to clarify the situation. It is based on different sources, partly on translation from English. All so that next time I won’t have to figure it out again - “what’s the point!?”

Needed PHP version depends on the version of the web server on which it will be used. For example, Apache 1.3.x works with PHP version 3.0.x, Apache 2.x works with PHP version 4.0 and higher. But this is not such a problem, focus on newer stable releases and what the hoster has.

What kind of postscripts VC6, VC9, VC11? PHP sources for Windows are compiled into Visual Studio. VC9 is obtained when compiled in VS 2008, VC11 - Visual Studio 2012. Accordingly, for this whole thing to work for you, libraries must be installed on your computer Visual C++ Redistributable for Visual Studio the corresponding year. Some clarification on this matter.

In addition, if your web server is an old Apache from apache.org, then you need to download VC6 PHP versions, for compilation of which Visual Studio 6 was used. If PHP works for IIS or in conjunction with a newer Apache, then you can build something more modern;)

For me, the main obstacle in choosing is the hoster. Now there is a stable version of PHP 5.5.4, but he still has 5.2.17!

Now the most interesting part: " thread safe or non thread safe?"
Free translation of the article (Dominic Ryan, 09/27/2007)

I have never seen such broken English:((I wanted to quickly translate the article, but I have difficulty understanding what the author wrote. The constant transitions between “what-is-that” and complex sentences generally make Moscow stand out. Translation into Russian is the same complicated by the fact that I do not have enough knowledge and imagination on how to correctly call something in Russian that is usually written only in English%) For example, I have never seen the technical concept of “multi process architecture” in Russian, but my pearl is “flow- unsafe" is generally a question of common sense. In general, I’ll give you what happened.

Difference between thread safe And non thread safe PHP binary packages

Since PHP first appeared on Windows on October 20, 2000 with PHP 3.0.17, its binary packages have always been built as thread safe (TS). The reason is as follows: Windows uses a multi-threaded architecture, and Nix systems support a multi-process architecture. If PHP was compiled as a multi-process CGI application instead of a multi-threaded one, then using it as a CGI module under Windows on an IIS server leads to severe slowdowns and CPU usage. On the other hand, you can connect PHP to IIS as an ISAPI module ( multi-threaded build required- approx. translator). Then another problem arises: some popular PHP extensions designed with a focus on Unix/Linux, i.e. with a multi-process architecture, which leads to the crash of PHP connected to IIS as an ISAPI module. That. CGI creation is the most stable environment for PHP on IIS with the main drawback that it is terribly slow. We have to load and unload the entire PHP environment from memory every time there is a request.

At the time, there were several options to improve PHP performance on IIS. The first is to use opcode caching with programs like eAccelerator, which store PHP scripts in a partially compiled state on disk and/or in memory. This approach significantly reduces script execution time. Another option was to configure IIS to using PHP in mode FastCGI. In this case, the PHP process did not close after completion, but received a new task with the next PHP request. In addition, it was possible to run several PHP processes at the same time, significantly speeding up the processing of requests, which was a bonus of the PHP CGI mode. However, there may have been minor compatibility issues with PHP extensions. It's still the most quick way use of PHP, and the “IIS Aid PHP Installer” installer is configured to set this IIS configuration.

Binaries collected in thread-unsafe mode (non thread safe, NTS), allow you to configure IIS (and other web servers on Windows) to use PHP as a standard CGI interface with a strong performance boost, because in this case (in such a build), the PHP process does not need to wait for threads to synchronize. When comparing the performance of "thread safe" and "non thread safe" PHP binary packages on IIS as a standard CGI interface, the performance increase is up to 40%, but it is still not as fast as using an opcode in the FastCGI method. And the biggest problem is that you cannot reliably use thread-unsafe binaries together with thread-safe ones. This means that you cannot use opcode caching systems like eAccelerator in PHP environment, created by thread-unsafe binary packages (a statement true at the time of writing).

If thread-unsafe PHP cannot be configured to the same speed as a thread-safe environment, then why is it needed in such a build? Let's go back to FastCGI and Microsoft developments in this area over the past few years. Small-soft coders have created their own version of FastCGI, which allows you to configure thread-unsafe PHP binaries in FastCGI mode, which brings performance to the speed of light :)

From the article I concluded that the brakes are observed only when used with the IIS web server. In any case, I haven’t seen any stupid things under Windows+Apache. It also says that you can overclock the NTS assembly by any web server, but I can't imagine such an Apache config.

The mother of a good friend of mine lost her suitcase on a flight from St. Petersburg to Moscow, and she had a further flight to warmer shores, and now she was already at the resort - without a swimsuit, sandals and with only a T-shirt from her hand luggage. For old times’ sake, I gave her a couple of tips on what to do and where to run, and today I decided to write everything I know on a given topic here.

To explain where I’m so smart, let me remind you that at one time I worked in ground handling for several airlines, including dealing with certain issues related to baggage search. Well, that's a plus own experience flights, of course. However, because I left the aviation service industry several years ago, perhaps some nuances might have changed - if so, I will gratefully accept comments on the topic and correct the information in the post.

I'll start with this What you need to do to prevent your luggage from being lost:
1. Tear off all tags and stickers from previous trips from your suitcase, even small ones with a bar code, which are often glued separately to the suitcase itself - they can be confusing automatic system scanning and sorting of luggage.
2. Hang a name tag on your suitcase (bag, box, package - in general, everything that you check in as luggage): you can buy a reusable option in advance or take a paper tag at the check-in counter - usually all more or less decent airlines give them out without restrictions . And Emirates, for example, generally has excellent plastic tags on a durable cord that can last a very long time:

Old paranoids can do like me: I always have a reusable plastic tag from the Samsonite set hanging on my suitcase with my permanent home address, phone number and by email, and when I fly somewhere on vacation, I additionally hang up a paper note on which I indicate the dates of my stay in the new place and all possible contacts (the name and address of the hotel, its local telephone number, if available, and my first and last name, of course ).
3. At the check-in counter, make sure that the baggage tag printed by the check-in agent is affixed to your luggage - with the city code to which you are flying and the flight number.
4. If you have several connecting flights, tell the check-in agent about this and specify to which point you want to check your luggage. IN in some cases luggage will have to be picked up at one or another airport along the route, regardless of your desire: this applies, for example, to transfers between airports (Orly and Charles de Gaulle in Paris, Domodedovo - Sheremetyevo - Vnukovo " in Moscow), separate terminals (terminals 1 and 2 in Frankfurt) or at the first point of arrival in the USA or Mexico - this is a customs requirement in these countries: suppose you are flying Moscow-Washington-Phoenix, a baggage tag is issued for all three segments up to Phoenix, but in Washington, your luggage will need to be physically picked up, cleared through customs, and checked in again. Also, if you are checking in a baby stroller that you were allowed to take on board the plane, or an animal, you may need to pick it up at a transit point. In general, in the case of a complex route with transfers, it is better to clarify the details of baggage movements in advance at the airline’s call center or, in extreme cases, at check-in.
5. Make your luggage visible: baggage delays are not always the fault of baggage handlers or failures in the sorting system. Sometimes another absent-minded passenger, tired after a long flight, will take from the luggage carousel the same black Samsonite or nondescript sports bag as yours. Therefore, mark your luggage: hang a bunch of bright ribbons or a small soft toy on the handle, stick a large sticker on it, or simply give preference to an unusual color when choosing a suitcase.

What should not be checked in luggage?
Remember, all airlines and airports lose luggage. Of course, statistics are different for everyone, but even the most reliable airlines can lose or delay luggage, and even at the smallest airport, where a single baggage handler will transport a cart with suitcases directly from the check-in counter to the plane. Therefore, I advise you to always take in your hand luggage:
- important documents, including those that are not needed during the flight (for example, on my last trip to St. Petersburg I needed to change my license, and I took with me in my hand luggage a marriage certificate and all sorts of cards from a driving school)
- keys (in combination with a tag with your address, this can be dangerous)
- money, jewelry (no comments)
- expensive fragile equipment
- medications that you regularly take, in the amount required for the flight, and with a small reserve in case you have to look for an analogue in a foreign country or city. Prescription medications that cannot be purchased if your luggage is lost, take with you in the amount necessary for the entire trip.
- something that may be needed urgently upon arrival (for example, Charger for phone
- something that has sentimental value for you personally: sometimes luggage is lost forever, and if the loss personal diary will break your heart, better leave it at home or take it with you on the plane

An instructive story: during the time of my work at Lufthansa in St. Petersburg, a married couple from the USA came running to our office, wringing their hands - their luggage, which contained very important documents for the adoption trial, had not arrived, the trial was the next day. Of course, the airline is to blame for the loss of luggage, but who benefits from this? To avoid such a situation, it was enough to simply put important papers in your hand luggage.

So, you arrived and did not find your luggage on the baggage belt. What to do?
1. If you checked in your luggage something different from ordinary suitcases: skis, a cello, a wall-sized plasma panel, a baby stroller, a live marble Great Dane, check if there is a separate point for issuing the so-called. oversized luggage or Bulky baggage - luggage similar to the one I described above is often loaded into a separate compartment and unloaded separately, manually. If your luggage is not found there either
2. Go to the baggage tracing or Lost & Found counter. There you will need to fill out special form With detailed information about your luggage: route, appearance, a short list of contents, your contacts at your permanent place of residence and temporary stay. Also, in the baggage tracing service you are more likely to see a baggage chart like this:

It is according to this classification that your missing luggage will be coded and, so you understand, these two suitcases will be coded the same:

So feel free to add additional details in the description and don't skip the content clause. As a rule, when you first fill out a baggage delay report, you will be asked to indicate several items of contents by which your bag can be identified if there are no identifying marks on the outside and the bag will have to be opened (if the bag is opened, you will put notification about this). Bad example: T-shirt / book / wet wipes, good example: bright red bikini / catalog of Malevich reproductions / folding iron. After filling out the application, the baggage tracing service employee will give you a number in the format XXXYY11111, where XXX is the arrival airport code, YY is the arrival airline code + 5 digits of the application serial number: for example, JFKLH12345, if you flew with Lufthansa to Kennedy Airport in New York. Remember or write down this number - it will be the easiest way to find your application in future applications.
Using the same number you can check the search status YOURSELF (for some reason the link disappears: if it doesn’t work for you, google World Tracer Online and literally the second link - with the heading Baggage Tracing on the website worldtracer.aero - is what you need), because getting through to lost&found is often very difficult
3. Try contacting the office of your airline at the airport of arrival: sometimes (I emphasize - SOMETIMES!) if you have not flown home, but to a place of temporary stay (vacation, business trip), the airline can provide a set of toiletries (Lufthansa has it). included an oversized T-shirt, a toothbrush and paste, a comb, small packages of shampoo and shower gel, a packet of washing powder, etc.) or make a small cash payment for small expenses on the spot (spot cash payment).

What will happen next?
Your file (the so-called AHL) will go to the centralized baggage search system (World Tracer). All unclaimed items of luggage fall into the same search system, no matter whether they were found without a tag in the nooks and crannies of the baggage yard or remained on the baggage belt; for each of these items, a file of the XXXYY11111 format is also created, only of a different subtype - the so-called. on-hand report or OHD. If the data from the AHL and OHD files coincide (last name, description of the suitcase, route, etc.), both stations (where the loss of luggage was reported and where the unclaimed luggage was found) will receive a notification, and then it’s a matter of technology: re-checking and in case of success forwarding luggage to desired city. Of course, a large number of self made- exchanging messages, rejecting similar but not the same suitcases, plus answering multiple phone calls - in general, the baggage tracing service staff never gets bored.
Approximate statistics: more than 90% of lost luggage is found in the first 3 days of search, 3% are lost forever.
What can you do?
1. If you have to buy anything urgently needed upon arrival (from a toothbrush to a business suit), be sure to keep your receipts for later compensation. However, you should avoid unnecessary expensive purchases; I’ll explain why later.
2. Following fresh steps, make the most detailed list of contents, preferably with the color, brand and approximate cost of each item, ideally for English language(because otherwise an airline employee will have to translate this list for inclusion in the system), contact the airline and send them this list, it will be added to the baggage search application. For the first 5 days, the search for luggage is carried out by the airport of arrival, then the search becomes the responsibility of the carrier airline (the airline that is indicated in the application number - remember JFKLH12345?), and after 21 days you can apply for final compensation.
3. If, after 21 days from the date of filing a statement about lost luggage, it has not been found, contact the carrier airline to demand compensation. If I'm not mistaken, the statute of limitations is 2 years, i.e. You can apply for compensation within two years from the date of filing a claim for loss.

Payment of compensation.
To pay compensation, you will need to contact the representative office of your airline with an application for payment, documents confirming the flight and the fact of loss of baggage (boarding passes, baggage tags, claim number for loss of baggage, payment details). If I'm not mistaken, in the Russian Federation a decision on compensation must be legally considered within 30 days. You may also be asked to estimate the cost of the contents and, if possible, provide receipts for the purchase of the suitcase and the items in it (I understand that this is unrealistic in most cases, but this is part of the procedure).
Previously, payments were made based on the weight of checked luggage - about $20 per kilogram. Later, the payment system was changed and the liability of airlines was limited to 1,000 conventional units (the cost of a conventional unit is calculated within the airline), which at the time of my work corresponded to approximately 1,300 euros. Those. even if you bring a receipt for the purchase of a Louis Vuitton suitcase made from a thousand Bolivian gecko skins and stuffed with diamonds, you won’t get more than 1,300 euros.

It seems that PHP developers rarely use concurrency. I won’t talk about the simplicity of synchronous code; single-threaded programming is, of course, simpler and clearer, but sometimes a little use of parallelism can bring a noticeable increase in performance.

In this article, we'll take a look at how multithreading can be achieved in PHP using the pthreads extension. To do this, you will need the ZTS (Zend Thread Safety) version of PHP 7.x installed, along with installed extension pthreads v3. (At the time of writing, in PHP 7.1, users will need to install from the master branch in the pthreads repository - see third party extension.)

A small clarification: pthreads v2 is intended for PHP 5.x and is no longer supported, pthreads v3 is for PHP 7.x and is actively being developed.

After such a digression, let's get straight to the point!

Processing one-time tasks

Sometimes you want to process one-time tasks in a multi-threaded way (for example, executing some I/O-bound task). In such cases, you can use the Thread class to create a new thread and run some processing on a separate thread.

For example:

$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"

Here the run method is our processing, which will be executed inside a new thread. When Thread::start is called, a new thread is spawned and the run method is called. We then join the child thread back to the main thread by calling Thread::join , which will block until the child thread has finished executing. This ensures that the task finishes executing before we try to print the result (which is stored in $task->response).

It may not be desirable to pollute a class with additional responsibilities associated with flow logic (including the responsibility of defining a run method). We can distinguish such classes by inheriting them from the Threaded class. Then they can be run inside another thread:

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);

Any class that needs to be run in a separate thread must inherit from the Threaded class. This is because it provides the necessary capabilities to perform processing on different threads, as well as implicit security and useful interfaces (such as resource synchronization).

Let's take a look at the class hierarchy offered by the pthreads extension:

Threaded (implements Traversable, Collectable) Thread Worker Volatile Pool

We've already covered and learned the basics of the Thread and Threaded classes, now let's take a look at the other three (Worker, Volatile, and Pool).

Reusing Threads

Starting a new thread for each task that needs to be parallelized is quite expensive. This is because a common-nothing architecture must be implemented in pthreads to achieve multithreading within PHP. Which means that the entire execution context of the current instance of the PHP interpreter (including every class, interface, trait, and function) must be copied for each thread created. Because this has a noticeable performance impact, the stream should always be reused whenever possible. Threads can be reused in two ways: using Workers or using Pools.

The Worker class is used to perform a number of tasks synchronously within another thread. This is done by creating a new Worker instance (which creates a new thread), and then pushing tasks onto that separate thread's stack (using Worker::stack).

Here's a small example:

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();

In the example above, 15 tasks for a new $worker object are pushed onto the stack via the Worker::stack method, and then they are processed in the order they were pushed. The Worker::collect method, as shown above, is used to clean up tasks as soon as they finish executing. With it, inside a while loop, we block the main thread until all tasks on the stack are completed and cleared - before we call Worker::shutdown . Terminating a worker early (i.e. while there are still tasks that need to be completed) will still block the main thread until all tasks have completed their execution, just that the tasks will not be garbage collected (which entails represent memory leaks).

The Worker class provides several other methods related to its task stack, including Worker::unstack for removing the last stacked task and Worker::getStacked for getting the number of tasks in the execution stack. A worker's stack contains only the tasks that need to be executed. Once a task on the stack has been completed, it is removed and placed on a separate (internal) stack for garbage collection (using the Worker::collect method).

Another way to reuse a thread across multiple tasks is to use a thread pool (via the Pool class). A thread pool uses a group of Workers to enable tasks to be executed simultaneously, in which the concurrency factor (the number of pool threads it operates with) is set when the pool is created.

Let's adapt the above example to use a pool of workers:

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();

There are a few notable differences when using a pool as opposed to a worker. First, the pool does not need to be started manually; it starts executing tasks as soon as they become available. Secondly, we send tasks to the pool, not put them on a stack. Additionally, the Pool class does not inherit from Threaded and therefore cannot be passed to other threads (unlike Worker).

It is a good practice for workers and pools to always clean up their tasks as soon as they have completed, and then manually terminate them themselves. Threads created using the Thread class must also be attached to the parent thread.

pthreads and (im)mutability

The last class we'll touch on is Volatile, a new addition to pthreads v3. Immutability has become an important concept in pthreads because without it, performance suffers significantly. Therefore, by default, the properties of Threaded classes that are themselves Threaded objects are now immutable, and therefore they cannot be overwritten after their initial assignment. Explicit mutability for such properties is currently preferred, and can still be achieved using the new Volatile class.

Let's look at an example that will demonstrate the new immutability restrictions:

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 properties of Volatile classes, on the other hand, are mutable:

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(); ) );

We can see that the Volatile class overrides the immutability imposed by the parent Threaded class to provide the ability to change Threaded properties (as well as unset()).

There is another subject of discussion to cover the topic of variability and the Volatile class - arrays. In pthreads, arrays are automatically cast to Volatile objects when assigned to a property of the Threaded class. This is because it is simply not safe to manipulate an array of multiple PHP contexts.

Let's look at an example again to understand some things better:

$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(); /* Output: Volatile Object ( => 1 => 2 => 3 => 4 => 5) */

We see that Volatile objects can be treated as if they were arrays because they support array operations such as (as shown above) the subset() operator. However, Volatile classes do not support basic array functions such as array_pop and array_shift. Instead, the Threaded class provides us with such operations as built-in methods.

As a demonstration:

$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); /* Output: 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) ) */

Other supported operations include Threaded::chunk and Threaded::merge .

Synchronization

In the last section of this article, we will look at synchronization in pthreads. Synchronization is a method that allows you to control access to shared resources.

For example, let's implement a simple counter:

$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); // will print a number from 10 to 20

Without the use of synchronization, the output is not deterministic. Multiple threads write to the same variable without controlled access, which means updates will be lost.

Let's fix this so that we get the correct output of 20 by adding timing:

$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)

Synchronized code blocks can also communicate with each other using the Threaded::wait and Threaded::notify (or Threaded::notifyAll) methods.

Here is a sequential increment in two synchronized while loops:

$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(); /* Output: 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) */

You may notice additional conditions, which were placed around the call to Threaded::wait . These conditions are critical because they allow the synchronized callback to resume when it has received a notification and the specified condition is true . This is important because notifications can come from places other than when Threaded::notify is called. Thus, if calls to the Threaded::wait method were not enclosed in conditions, we will execute false wake up calls, which will lead to unpredictable code behavior.

Conclusion

We looked at the five classes of the pthreads package (Threaded, Thread, Worker, Volatile, and Pool) and how each class is used. We also looked at the new concept of immutability in pthreads, made short review supported synchronization capabilities. With these basics in place, we can now start looking at how pthreads can be used in real world cases! This will be the topic of our next post.

If you are interested in the translation of the next post, let me know: comment on social media. networks, upvote and share the post with colleagues and friends.

Sometimes it becomes necessary to perform several actions simultaneously, for example, checking changes in one database table and making modifications to another. Moreover, if one of the operations (for example, checking changes) takes a lot of time, it is obvious that sequential execution will not ensure resource balancing.

To solve this kind of problem, programming uses multithreading - each operation is placed in a separate thread with an allocated amount of resources and works within it. With this approach, all tasks will be completed separately and independently.

Although PHP does not support multithreading, there are several methods for emulating it, which will be discussed below.

1. Running several copies of the script - one copy per operation

//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( ); )

When we execute this script without parameters, it automatically runs two copies of itself, with operation IDs ("thread=make_me_happy" and "thread=make_me_rich"), which initiate the execution of the necessary functions.

This way we achieve the desired result - two operations are performed simultaneously - but this, of course, is not multithreading, but simply a crutch for performing tasks simultaneously.

2. Path of the Jedi - using the PCNTL extension

PCNTL is an extension that allows you to fully work with processes. In addition to management, it supports sending messages, checking status and setting priorities. This is what the previous script using PCNTL looks like:

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

It looks quite confusing, let's go through it line by line.

In the first line, we “fork” the current process (fork is copying a process while preserving the values ​​of all variables), dividing it into two processes (current and child) running in parallel.

To understand where we are at this moment, in a child or mother process, the pcntl_fork function returns 0 for the child and the process ID for the mother. Therefore, in the second line, we look at $pid, if it is zero, then we are in the child process - we are executing the function, otherwise, we are in the mother (line 4), then we create another process and similarly perform the task.

Script execution process:

Thus, the script creates 2 more child processes, which are its copies and contain the same variables with similar values. And using the identifier returned by the pcntl_fork function, we find out which thread we are currently in and perform the necessary actions.

I recently tried pthreads and was pleasantly surprised - it's an extension that adds the ability to work with multiple real threads in PHP. No emulation, no magic, no fakes - everything is real.



I am considering such a task. There is a pool of tasks that need to be completed quickly. PHP has other tools for solving this problem, they are not mentioned here, the article is about pthreads.



What are pthreads

That's all! Well, almost everything. In fact, there is something that may upset an inquisitive reader. None of this works on standard PHP compiled with default options. To enjoy multithreading, you must have ZTS (Zend Thread Safety) enabled in your PHP.

PHP setup

Next, PHP with ZTS. Don't mind the big difference in execution time compared to PHP without ZTS (37.65 vs 265.05 seconds), I didn't try to generalize the PHP setup. In the case without ZTS, I have XDebug enabled for example.


As you can see, when using 2 threads, the speed of program execution is approximately 1.5 times higher than in the case of linear code. When using 4 threads - 3 times.


You can note that even though the processor is 8-core, the execution time of the program remained almost unchanged if more than 4 threads were used. It seems that this is due to the fact that my processor has 4 physical cores. For clarity, I have depicted the plate in the form of a diagram.


Summary

In PHP, it is possible to work quite elegantly with multithreading using the pthreads extension. This gives a noticeable increase in productivity.

Tags: Add tags