Real PHP multithreading application

PHP Multithreading Application

Prelude

This article not about PThreads implementation. With this guide you will learn how to build multithreading app without PThreads and recompiling PHP

When you need to speed up your application, but you think that everything was optimized, you can increase performance by splitting processing data. For example, if you have a file with 1000 lines and you want to read them faster.

non-multithreading application

So, you can create, for example, 10 nodes that will take their own chunks, read the data and do the rest. In that case you will proceed file 10 times faster.

multithreading application

Difference between Multiprocessing and Multithreading

The difference is that threads run in the same memory space, while processes have separate memory. This makes it a bit harder to share objects between processes with multiprocessing.

Spawning processes is a bit slower than spawning threads. Once they are running, there is not much difference.

Multithreading PHP application (PThreads)

PThreads (v3) can only be used with PHP 7.2+: This is due to ZTS mode being unsafe in 7.0 and 7.1.

To use PThreads you need a build of PHP with ZTS (Zend Thread Safety) enabled ( --enable-maintainer-zts or --enable-zts on Windows ). That's mean that you have to build (or rebuild) your PHP. This process can take about 30 minutes (depending on the hardware where you building it). Also, if you have additional extensions (especially PECL extensions) they might not work (or even compile) properly.

Considering these drawbacks most projects decide to avoid PThreads at all.

Multiprocess PHP application (PCNTL)

This is the most elementary method in many simple projects. We'll fork our PHP process using pcntl_fork, and perform some operations separately for each process.

When pcntl_fork called, main process' copy has been created. The main (parent) process will continue executing code inside block where $pid > 0 and will never know what's happened in the child's block. So, both processes created from one point, but become isolated. They don't have shared memory.

Sometimes we have a child process that takes longer execution time then parent's. Then we can use special function pcntl_wait for waiting all children processes to finish.

Cross-process data exchange

Forking process is simple. But composing them together impossible. And what if we need to receive data from child (or even few children process)?

Well, implementation could be based on several approaches: shared memory, temporary file, process-piping, etc. But if we need instant messaging, we'll use stream_socket_pair

cross-process data exchange

We've got another question: how to call reading procedure to interrupt the main process and get data from a child?

For this purpose, we can use pcntl_signal and posix_kill

pcntl_signal will handle signals sent to our process from outside and posix_kill will send signal to parent process that will initiate the process to read data from a child.

For example, we could use this syntax to create our "Threads":

As you might see, threading in PHP can be simple even we don't have ZTS version.

Bonus: working example with synchronizing

This code will output something like this: