How can one use multi threading in PHP applications
Asked Answered
B

17

504

Is there a realistic way of implementing a multi-threaded model in PHP whether truly, or just simulating it. Some time back it was suggested that you could force the operating system to load another instance of the PHP executable and handle other simultaneous processes.

The problem with this is that when the PHP code finished executing the PHP instance remains in memory because there is no way to kill it from within PHP. So if you are simulating several threads you can imagine whats going to happen. So I am still looking for a way multi-threading can be done or simulated effectively from within PHP. Any ideas?

Bullington answered 16/9, 2008 at 9:56 Comment(5)
See my question and answers here: #2102140Stumpage
...and mine here: https://mcmap.net/q/75245/-does-php-have-threading/…Ramillies
how to use pthreads extension: phplobby.com/php-multi-thread-on-windows-pthreads-configurationAshia
Maybe of interest: pthreads.orgIncommunicative
Now in 2020, it seems "parallel" php.net/manual/en/intro.parallel.php is what we want instead of "pthreads": https://mcmap.net/q/75246/-pthreads-vs-parallel-for-infinite-loops-on-phpErmey
R
508

Warning: This extension is considered unmaintained and dead. Warning: The pthreads extension cannot be used in a web server environment. Threading in PHP is therefore restricted to CLI-based applications only. Warning: pthreads (v3) can only be used with PHP 7.2+: This is due to ZTS mode being unsafe in 7.0 and 7.1.

https://www.php.net/manual/en/intro.pthreads.php


Multi-threading is possible in php

Yes you can do multi-threading in PHP with pthreads

From the PHP documentation:

pthreads is an object-orientated API that provides all of the tools needed for multi-threading in PHP. PHP applications can create, read, write, execute and synchronize with Threads, Workers and Threaded objects.

Warning: The pthreads extension cannot be used in a web server environment. Threading in PHP should therefore remain to CLI-based applications only.

Simple Test

#!/usr/bin/php
<?php
class AsyncOperation extends Thread {

    public function __construct($arg) {
        $this->arg = $arg;
    }

    public function run() {
        if ($this->arg) {
            $sleep = mt_rand(1, 10);
            printf('%s: %s  -start -sleeps %d' . "\n", date("g:i:sa"), $this->arg, $sleep);
            sleep($sleep);
            printf('%s: %s  -finish' . "\n", date("g:i:sa"), $this->arg);
        }
    }
}

// Create a array
$stack = array();

//Initiate Multiple Thread
foreach ( range("A", "D") as $i ) {
    $stack[] = new AsyncOperation($i);
}

// Start The Threads
foreach ( $stack as $t ) {
    $t->start();
}

?>

First Run

12:00:06pm:     A  -start -sleeps 5
12:00:06pm:     B  -start -sleeps 3
12:00:06pm:     C  -start -sleeps 10
12:00:06pm:     D  -start -sleeps 2
12:00:08pm:     D  -finish
12:00:09pm:     B  -finish
12:00:11pm:     A  -finish
12:00:16pm:     C  -finish

Second Run

12:01:36pm:     A  -start -sleeps 6
12:01:36pm:     B  -start -sleeps 1
12:01:36pm:     C  -start -sleeps 2
12:01:36pm:     D  -start -sleeps 1
12:01:37pm:     B  -finish
12:01:37pm:     D  -finish
12:01:38pm:     C  -finish
12:01:42pm:     A  -finish

Real World Example

error_reporting(E_ALL);
class AsyncWebRequest extends Thread {
    public $url;
    public $data;

    public function __construct($url) {
        $this->url = $url;
    }

    public function run() {
        if (($url = $this->url)) {
            /*
             * If a large amount of data is being requested, you might want to
             * fsockopen and read using usleep in between reads
             */
            $this->data = file_get_contents($url);
        } else
            printf("Thread #%lu was not provided a URL\n", $this->getThreadId());
    }
}

$t = microtime(true);
$g = new AsyncWebRequest(sprintf("http://www.google.com/?q=%s", rand() * 10));
/* starting synchronization */
if ($g->start()) {
    printf("Request took %f seconds to start ", microtime(true) - $t);
    while ( $g->isRunning() ) {
        echo ".";
        usleep(100);
    }
    if ($g->join()) {
        printf(" and %f seconds to finish receiving %d bytes\n", microtime(true) - $t, strlen($g->data));
    } else
        printf(" and %f seconds to finish, request failed\n", microtime(true) - $t);
}
Rockaway answered 19/3, 2013 at 13:55 Comment(19)
@Baba, I'm not able to configure and install pthreads on Xampp server. Can you help me with that?Begum
Download windows binary here windows.php.net/downloads/pecl/releases/pthreads/0.0.45Rockaway
@Rockaway can you access shared SESSION variables with multi-thread?Mellman
@danip - MPM worker for Apache has nothing to do with PHP userland threading, which is what Baba is talking about. Those are two entirely different things. MPM worker for Apache doesn't expose any threading functionality for PHP programmers, in terms of splitting units of processing across threads and then join()-ing them with main context when they're done. What MPM does is simply distribute requests in a more efficient manner to processes/threads running PHP process.Singspiel
That's nice, I have not touched PHP for years and now it's got multithreading capabilities!Izy
Nice and simple! Just FYI, I am deploying an app on Azure Cloud Win server and if only the basic 1 core configuration is selected the multi-threading will not be available unless more cores are added.Savory
I am having trouble finding an answer to my question so I will post here...(PHP seems to be a very emotionally charged language and the level of subjective rhetoric on the subject is... well... frustrating, I digress...) The documentation mentions that it allows for user-land threads. My question is, what about within the kernel? Is PHP truly multi threaded or is it solely in userland? Documentation on the matter would be greatly appreciated as well.Repetend
Please take a look here #28493921 I'm trying to work multithreading but i'm not sure it is working and taking more time than expected.Wembley
@Rockaway iam using ubuntu 14.04 system. how to configure pthread in it.Seftton
Lets say you have a php process which spawns a few threads to carry out some operations which might also involve calls to DB. Now if there is a SQL exception in 1 thread, does it crash the entire system?Invigilate
Why there is a weaning on not using pthreads for web and to be used only for CLI secure.php.net/manual/en/intro.pthreads.phpCarbon
@Rockaway can i run thread by HTTP ? simple : yourname.com/thread.php ?Drawl
It says that needs 7.2+ but downloads are only for 7.1.9. I'm running win32 cli php on 7.1.9, how can i use this?Wart
In the PHP 7.2.4 (cli) ( ZTS MSVC15 (Visual C++ 2017) x64 ) I just see start and don't echo finish why?!Grammer
Please beware: Joe Watkins, the author of pthreads extension discontinued the development in favor of the new parallel extension: github.com/krakjoe/pthreads/issues/929Kekkonen
pThreads is discontinued. Use parallel from PHP 7.4.Expedition
OTOH, parallel needs a recompiled version of PHP is my understanding? huge disruptive task if you already have a production server... any other feasible alternatives?Shelburne
@JoseCifuentes On this page: php.net/manual/en/parallel.setup.php is written: Windows users can download prebuilt release binaries from the » PECL website.Moslem
The developer of parallel (which is the same lead dev, who created pThreads) wrote in August 2021 on his GitHub repository, that he doesn't use parallel for anything anymore and says that the project is "probably a dead end". It wasn't updated for 2 years. There are no pre-compiled versions for Windows for PHP 8.x and the code doesn't seem to compile either.Glottis
D
65

why don't you use popen?

for ($i=0; $i<10; $i++) {
    // open ten processes
    for ($j = 0; $j < 10; $j++) {
        $pipe[$j] = popen('script2.php', 'w');
    }

    // wait for them to finish
    for ($j = 0; $j < 10; ++$j) {
        pclose($pipe[$j]);
    }
}
Dina answered 3/12, 2010 at 22:27 Comment(9)
I'm using the solution above, and works fine, I think that it was the most easy way to do parallel process using php.Gnome
like @e-info128 said, this implementation forks the process, which means that it is running on a different process, and does not share process resources. That being said, if the job at hand does not need to share resources, then this will still work and it will run in parallel.Morbid
How would you pass variables to popen without using session variables?Seda
@Seda No way, these are separate processes sharing no resources. Even sessions will be awkward IPC mechanismEvita
@e-info128 what's a fork?Skipper
To pass data to them, you can use arguments and Redis server too.Zara
@Skipper A fork is usually a child process spawned by the parent. So here its being suggested that the single threaded PHP application could create another PHP process (a fork) and use that like a fake threading approachMalapropos
I have a cron script that runs every minute. The task list has grown large enough that it needs more than a minute every time it runs. This would be perfect to split the tasks without manually adding more cron scripts. Thanks.Converted
I tried this code, but I'm getting a "sh: 1: script2.php: not found" error. The file indeed exists. I tried the full path to the file as well, same error. Any idea on what could be wrong?Hola
D
29

Threading isn't available in stock PHP, but concurrent programming is possible by using HTTP requests as asynchronous calls.

With the curl's timeout setting set to 1 and using the same session_id for the processes you want to be associated with each other, you can communicate with session variables as in my example below. With this method you can even close your browser and the concurrent process still exists on the server.

Don't forget to verify the correct session ID like this:

http://localhost/test/verifysession.php?sessionid=[the correct id]

startprocess.php

$request = "http://localhost/test/process1.php?sessionid=".$_REQUEST["PHPSESSID"];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $request);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 1);
curl_exec($ch);
curl_close($ch);
echo $_REQUEST["PHPSESSID"];

process1.php

set_time_limit(0);

if ($_REQUEST["sessionid"])
   session_id($_REQUEST["sessionid"]);

function checkclose()
{
   global $_SESSION;
   if ($_SESSION["closesession"])
   {
       unset($_SESSION["closesession"]);
       die();
   }
}

while(!$close)
{
   session_start();
   $_SESSION["test"] = rand();
   checkclose();
   session_write_close();
   sleep(5);
}

verifysession.php

if ($_REQUEST["sessionid"])
    session_id($_REQUEST["sessionid"]);

session_start();
var_dump($_SESSION);

closeprocess.php

if ($_REQUEST["sessionid"])
    session_id($_REQUEST["sessionid"]);

session_start();
$_SESSION["closesession"] = true;
var_dump($_SESSION);
Del answered 16/9, 2008 at 9:56 Comment(5)
Last time i checked (a few years ago) php didn't allow to access file based session storage by two processes simultaneously. It locks file and second process has to sit there waiting for the first script to stop. I'm talking about webserver environment, not CLI.Hemminger
set_time_limit(0); yikes! Never, ever do this.Nimitz
@Nimitz Kafoso why not? Well I agree for PHP as a web script processor, but why not in CLI? If something goes wrong, CLI can be killed with Ctrl+C...Flatworm
I would also use set_time_limit(0); for infinitely running socket listeners..Grillroom
set_time_limit refreshes timeout, so might be reasonable to put it in the while loop with some adequate timeout, say 30 seconds here.Iorgos
C
17

While you can't thread, you do have some degree of process control in php. The two function sets that are useful here are:

Process control functions http://www.php.net/manual/en/ref.pcntl.php

POSIX functions http://www.php.net/manual/en/ref.posix.php

You could fork your process with pcntl_fork - returning the PID of the child. Then you can use posix_kill to despose of that PID.

That said, if you kill a parent process a signal should be sent to the child process telling it to die. If php itself isn't recognising this you could register a function to manage it and do a clean exit using pcntl_signal.

Clyde answered 16/9, 2008 at 10:30 Comment(2)
That answer is now very outdated ( which is very fair knowing that it is 11 years old ). Look at pthreads below.Pavement
@MaciejPaprocki pThread is now discontinued fromphp 7.4 instead use parallelBaugher
K
13

using threads is made possible by the pthreads PECL extension

http://www.php.net/manual/en/book.pthreads.php

Khalkha answered 31/10, 2013 at 18:7 Comment(1)
"pThread is now discontinued from php 7.4 instead use parallel".Expedition
S
12

You can use exec() to run a command line script (such as command line php), and if you pipe the output to a file then your script won't wait for the command to finish.

I can't quite remember the php CLI syntax, but you'd want something like:

exec("/path/to/php -f '/path/to/file.php' | '/path/to/output.txt'");

I think quite a few shared hosting servers have exec() disabled by default for security reasons, but might be worth a try.

Sharpedged answered 16/9, 2008 at 14:3 Comment(0)
B
12

I know this is an old question but for people searching, there is a PECL extension written in C that gives PHP multi-threading capability now, it's located here https://github.com/krakjoe/pthreads

Bundesrat answered 19/9, 2012 at 2:39 Comment(1)
pThread is now discontinued from php 7.4 instead use parallelBaugher
C
6

You could simulate threading. PHP can run background processes via popen (or proc_open). Those processes can be communicated with via stdin and stdout. Of course those processes can themselves be a php program. That is probably as close as you'll get.

Cockleboat answered 26/8, 2010 at 21:33 Comment(0)
S
6

You can have option of:

  1. multi_curl
  2. One can use system command for the same
  3. Ideal scenario is, create a threading function in C language and compile/configure in PHP. Now that function will be the function of PHP.
Sonnie answered 10/5, 2011 at 7:40 Comment(0)
U
6

How about pcntl_fork?

check our the manual page for examples: PHP pcntl_fork

<?php

    $pid = pcntl_fork();
    if ($pid == -1) {
        die('could not fork');
    } else if ($pid) {
        // we are the parent
        pcntl_wait($status); //Protect against Zombie children
    } else {
        // we are the child
    }

?>
Ulcerate answered 13/1, 2012 at 0:55 Comment(0)
B
6

If you are using a Linux server, you can use

exec("nohup $php_path path/script.php > /dev/null 2>/dev/null &")

If you need pass some args

exec("nohup $php_path path/script.php $args > /dev/null 2>/dev/null &")

In script.php

$args = $argv[1];

Or use Symfony https://symfony.com/doc/current/components/process.html

$process = Process::fromShellCommandline("php ".base_path('script.php'));
$process->setTimeout(0);     
$process->disableOutput();     
$process->start();
Broddie answered 29/4, 2020 at 15:12 Comment(0)
B
5

Depending on what you're trying to do you could also use curl_multi to achieve it.

Bottomry answered 25/1, 2011 at 4:45 Comment(0)
S
4

pcntl_fork won't work in a web server environment if it has safe mode turned on. In this case, it will only work in the CLI version of PHP.

Schiffman answered 2/2, 2012 at 4:7 Comment(0)
T
3

I know this is an old question, but this will undoubtedly be useful to many: PHPThreads

Code Example:

function threadproc($thread, $param) {
 
    echo "\tI'm a PHPThread.  In this example, I was given only one parameter: \"". print_r($param, true) ."\" to work with, but I can accept as many as you'd like!\n";
 
    for ($i = 0; $i < 10; $i++) {
        usleep(1000000);
        echo "\tPHPThread working, very busy...\n";
    }
 
    return "I'm a return value!";
}
 

$thread_id = phpthread_create($thread, array(), "threadproc", null, array("123456"));
 
echo "I'm the main thread doing very important work!\n";
 
for ($n = 0; $n < 5; $n++) {
    usleep(1000000);
    echo "Main thread...working!\n";
}
 
echo "\nMain thread done working.  Waiting on our PHPThread...\n";
 
phpthread_join($thread_id, $retval);
 
echo "\n\nOur PHPThread returned: " . print_r($retval, true) . "!\n";

Requires PHP extensions:

  • posix
  • pcntl
  • sockets

I've been using this library in production now for months. I put a LOT of effort into making it feel like using POSIX pthreads. If you're comfortable with pthreads, you can pick this up and use it very effectively in no time.

Computationally, the inner workings are quite different, but practically, the functionality is nearly the same including semantics and syntax.

I've used it to write an extremely efficient WebSocket server that supports high throughput rates. Sorry, I'm rambling. I'm just excited that I finally got it released and I want to see who it will help!

Tiber answered 25/3, 2022 at 13:37 Comment(1)
I forgot to mention, I designed this for use in CLI mode. That doesn't mean you can't run it in CGI mode/through a web sever. You'd just have to get creative to allow the server's PHP module full access to PHPThread's required extensions and their respective functions. This library was designed for server-side applications/daemons.Tiber
D
0

popen()/proc_open() works parallel even in Windows.

Most often pitfall is "fread/stream_get_contents" without while loop. Once you try to fread() from running process it will block output for processes that run after it (cause of fread() waits until at least one byte arrives)

Add stream_select(). Closest analogy is "foreach with timeout but for streams", you pass few arrays to read and write and each call of stream_select() one or more streams will be selected. Function updates original arrays by reference, so dont forget to restore it to all streams before next call. Function gives them some time to read or write. If no content - control returns allowing us to retry cycle.

// sleep.php
set_error_handler(function ($severity, $error, $file, $line) {
    throw new ErrorException($error, -1, $severity, $file, $line);
});

$sleep = $argv[ 1 ];

sleep($sleep);

echo $sleep . PHP_EOL;

exit(0);
// run.php
<?php

$procs = [];
$pipes = [];

$cmd = 'php %cd%/sleep.php';

$desc = [
    0 => [ 'pipe', 'r' ],
    1 => [ 'pipe', 'w' ],
    2 => [ 'pipe', 'a' ],
];

for ( $i = 0; $i < 10; $i++ ) {
    $iCmd = $cmd . ' ' . ( 10 - $i ); // add SLEEP argument to each command 10, 9, ... etc.

    $proc = proc_open($iCmd, $desc, $pipes[ $i ], __DIR__);

    $procs[ $i ] = $proc;
}

$stdins = array_column($pipes, 0);
$stdouts = array_column($pipes, 1);
$stderrs = array_column($pipes, 2);

while ( $procs ) {
    foreach ( $procs as $i => $proc ) {
        // @gzhegow > [OR] you can output while script is running (if child never finishes)
        $read = [ $stdins[ $i ] ];
        $write = [ $stdouts[ $i ], $stderrs[ $i ] ];
        $except = [];
        if (stream_select($read, $write, $except, $seconds = 0, $microseconds = 1000)) {
            foreach ( $write as $stream ) {
                echo stream_get_contents($stream);
            }
        }

        $status = proc_get_status($proc);

        if (false === $status[ 'running' ]) {
            $status = proc_close($proc);
            unset($procs[ $i ]);

            echo 'STATUS: ' . $status . PHP_EOL;
        }

        // @gzhegow > [OR] you can output once command finishes
        // $status = proc_get_status($proc);
        //
        // if (false === $status[ 'running' ]) {
        //     if ($content = stream_get_contents($stderrs[ $i ])) {
        //         echo '[ERROR]' . $content . PHP_EOL;
        //     }
        //
        //     echo stream_get_contents($stdouts[ $i ]) . PHP_EOL;
        //
        //     $status = proc_close($proc);
        //     unset($procs[ $i ]);
        //
        //     echo 'STATUS: ' . $status . PHP_EOL;
        // }
    }

    usleep(1); // give your computer one tick to decide what thread should be used
}

// ensure you receive 1,2,3... but you've just run it 10,9,8...

exit(0);
Diderot answered 16/10, 2022 at 11:5 Comment(0)
C
-2

Multithreading means performing multiple tasks or processes simultaneously, we can achieve this in php by using following code,although there is no direct way to achieve multithreading in php but we can achieve almost same results by following way.

chdir(dirname(__FILE__));  //if you want to run this file as cron job
 for ($i = 0; $i < 2; $i += 1){
 exec("php test_1.php $i > test.txt &");
 //this will execute test_1.php and will leave this process executing in the background and will go         

 //to next iteration of the loop immediately without waiting the completion of the script in the   

 //test_1.php , $i  is passed as argument .

}

Test_1.php

$conn=mysql_connect($host,$user,$pass);
$db=mysql_select_db($db);
$i = $argv[1];  //this is the argument passed from index.php file
for($j = 0;$j<5000; $j ++)
{
mysql_query("insert  into  test   set

                id='$i',

                comment='test',

                datetime=NOW() ");

}

This will execute test_1.php two times simultaneously and both process will run in the background simultaneously ,so in this way you can achieve multithreading in php.

This guy done really good work Multithreading in php

Complaisant answered 5/11, 2013 at 12:51 Comment(4)
Also, this has nothing todo with MultiThreading. This is parallel-processing. Totally different things.Laius
In my opinion as a workaround, an emergency hack, the idea behind the offered solution is very appropriate, but I guess different people can have flame wars about what constitutes "true multi-threading", because there is a distinction between concurrency and hardware based parallel processing, as described at: youtube.com/watch?v=cN_DpYBzKsoIncline
@MartinVahi the video is not available anymore, perhaps you've an alternate link?Moslem
@Moslem Sorry, no alternative link. Let's just say, it makes sense to create screen-recordings of YouTube videos and I do that for politically sensitive ones, but that one I haven't recorded. Unfortunately.Incline
I
-4

As of the writing of my current comment, I don't know about the PHP threads. I came to look for the answer here myself, but one workaround is that the PHP program that receives the request from the web server delegates the whole answer formulation to a console application that stores its output, the answer to the request, to a binary file and the PHP program that launched the console application returns that binary file byte-by-byte as the answer to the received request. The console application can be written in any programming language that runs on the server, including those that have proper threading support, including C++ programs that use OpenMP.

One unreliable, dirty, trick is to use PHP for executing a console application, "uname",

uname -a

and print the output of that console command to the HTML output to find out the exact version of the server software. Then install the exact same version of the software to a VirtualBox instance, compile/assemble whatever fully self-contained, preferably static, binaries that one wants and then upload those to the server. From that point onwards the PHP application can use those binaries in the role of the console application that has proper multi-threading. It's a dirty, unreliable, workaround to a situation, when the server administrator has not installed all needed programming language implementations to the server. The thing to watch out for is that at every request that the PHP application receives the console application(s) terminates/exit/get_killed.

As to what the hosting service administrators think of such server usage patterns, I guess it boils down to culture. In Northern Europe the service provider HAS TO DELIVER WHAT WAS ADVERTISED and if execution of console commands was allowed and uploading of non-malware files was allowed and the service provider has a right to kill any server process after a few minutes or even after 30 seconds, then the hosting service administrators lack any arguments for forming a proper complaint. In United States and Western Europe the situation/culture is very different and I believe that there's a great chance that in U.S. and/or Western Europe the hosting service provider will refuse to serve hosting service clients that use the above described trick. That's just my guess, given my personal experience with U.S. hosting services and given what I have heard from others about Western European hosting services. As of the writing of my current comment(2018_09_01) I do not know anything about the cultural norms of the Southern-European hosting service providers, Southern-European network administrators.

Incline answered 1/9, 2018 at 5:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.