A point which attracts most developers to PHP is the appeal of a simple synchronous, single-threaded programming. That is hard to miss of course but at times a little bit of addition of concurrency can bring about some significant performance improvements.
There are times when PHP developers need to execute multiple tasks in parallel and that means they need to deploy some amount of multithreading solutions.
Today, in particular, we are going to be talking about the pthreads extension. If one had to describe it in the simplest of terms then the answer would be- “It is an extension that allows people to work in PHP by running multiple tasks in parallel within the same process.
What Are Pthreads?
This is an object-oriented API that gives developers an extremely convenient way to organize multi-threaded tasks in PHP. This API also includes all the tools that are required to create multi-threaded applications. PHP applications have the capability to create, read, execute, write, and synchronize threads using objects of three classes- worker, Thread and threaded.
The Hierarchy?
There is a hierarchy of basic classes that needs to be followed and that is:
Threaded
This is a pthread basis. It is what makes it possible to run code in parallel. It is also responsible for providing methods for synchronization and all other techniques.
Thread
You are supposed to create a subclass of Thread and then implement the method run(). This method executes a separate thread where you call the start() method. This is only initiated from a process that has created the thread. Combining threads is also carried out in this, the same process.
Worker
These threads have a context that can be used by different threads. This is only available, however, when the worker object has references to it and only until the shutdown() method is called.
There is another addition to these classes, which is the ...
Pool
This is a container of workers, that are used for the distribution job amongst the worker objects. A Pool class is the simplest, most effective way to organize several threads.
When Do I Avoid Pthreads?
Before we jump into the live examples, I would like to point out all the times that you should not and also cannot use the pthreads extension.
In pthreads v2 it was recommended that a person should not use this extension in a web server environment. When pthreads v3 came into play it was clarified that you simply could not any longer use it in a web server environment.
There are two major reasons for this to happen,
-
You should not use multiple threads in such an environment as it is not safe. ( it might lead to IO issues, along with a horde of other problems)
-
It never manages to scale too well.
This is why you should best avoid it when it comes to an environment like that. If you are still interested in a threading solution regarding IO-blocking tasks then you should try out asynchronous programming. This can be achieved by a framework like Amp.
One-Off Tasks
There are going to be times when you will want to handle a one-off task in a multi-threaded way. For this, the THREAD class is useful to create a new thread and run some work in that separate thread.
E.g.
- $task = new class extends Thread {
- private $response;
- public
- function run() {
- $content = file_get_contents("http://google.com");
- preg_match("~<title>(.+)</title>~", $content, $matches);
- $this - > response = $matches[1];
- }
- };
- $task - > start() && $task - > join();
- var_dump($task - > response);
Here the run method is the unit of work that will take place in the new thread. When someone invokes the Thread:: start, then the run method is invoked.
This then joins back to the main thread which blocks all separate threads until the time the function is executed. This ensures the output is achieved.
It might not always be the best idea to pollute a class’s responsibility with all the logic required to run a thread. This is when all classes are segregated with the help of the Threaded class.
- class Task extends Threaded {
- public $response;
- public
- function someWork() {
- $content = file_get_contents('http://google.com');
- preg_match('~<title>(.+)</title>~', $content, $matches);
- $this - > response = $matches[1];
- }
- }
- $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 has to run separately must extend the Threaded class in one way or another.
Synchronization
One of the final things a person should understand when it comes to the case of pthreads is synchronization. This is a technique in which controlled access is granted to shared resources
To understand this we will look at a native counter
- $counter = new class extends Thread {
- public $i = 0;
- public
- function run() {
- for ($i = 0; $i < 10; ++$i) {
- ++$this - > i;
- }
- }
- };
- $counter - > start();
- for ($i = 0; $i < 10; ++$i) {
- ++$counter - > i;
- }
- $counter - > join();
- var_dump($counter - > i);
When you don’t indulge in synchronization, then the output does not turn out to be deterministic. Multiple threads thus turn into a single variable and there is no control which leads to loss of data.
This, however, can be easily fixed by adding synchronization to the mix for the correct output.
- $counter = new class extends Thread {
- public $i = 0;
- public
- function run() {
- $this - > synchronized(function() {
- for ($i = 0; $i < 10; ++$i) {
- ++$this - > i;
- }
- });
- }
- };
- $counter - > start();
- $counter - > synchronized(function($counter) {
- for ($i = 0; $i < 10; ++$i) {
- ++$counter - > i;
- }
- }, $counter);
- $counter - > join();
- var_dump($counter - > i);
Pthreads can enhance the experience of your website if implemented correctly. So, ensure that you utilize them correctly and provide your users with the best possible experience.