Improved Thread Simulation Class for PHP
Threads. Most PHP scripts do just fine without them. However, when you venture into the domain of complex and numerous database operations, or when you need to deal with datasources of high latency (to put it plainly, HTTP downloads), the multithreading concept starts to look more attractive.
Threading support in PHP is so-so, and the pcntl extension doesn’t work on Windows. Since I’m too lazy to boot up Linux just for the sake of PHP development, I once again went looking for ways to simulate threads (I’ve written about this before).
I created two classes that use asynchronous HTTP POST requests to simulate multithreading (inspired by this class). The Thread simulates a single thread of execution, whereas ThreadManager is an utility class that makes handling multiple threads easier. The script is a bit too long to post here, so …
Download It
thread.zip (3 KB)
Check the examples below for how to use the classes.
Usage Examples
Here you’ll find a few examples of how to use the two classes. You can also take a look inside thread.php for additional info - most class methods contain explanatory comments.
The next code block is common for all examples. In it I include the thread.php file to get access to the classes, and define the test() function which I will use as the example thread function. test($s) simply sleeps for $s seconds and returns a message telling how long it slept.
include_once("includes/thread.php"); function test($s){ sleep($s); return $s." seconds have passed."; }
Using the Thread class
This example will create two simulated threads and execute them simultaneously. Note that this isn’t the way I’d do it, as handling multiple threads this way is a hassle. However, it shows the basic functions of the class.
$program_start_time = microtime(true); //Create to thread $thread1 = new Thread; /*Set the thread function to execute, and it's arguments. Every argument should be passed as an element of the array. The thread function receives the arguments normally.*/ $thread1->setFunc('test', array(3)); //Start the thread $thread1->start(); $thread2 = new Thread; $thread2->setFunc('test', array(5)); $thread2->start(); //Wait while the threads are running while (!$thread1->finished || !$thread2->finished){ $thread1->query(); $thread2->query(); /* query($seconds, $useconds) waits for a specified time for the thread and processes it's output (if available), filling the Thread::response and Thread::result fields. Returns TRUE if the thread is still running, FALSE otherwise.*/ } //Output the results echo "Thread1 : ", $thread1->result,"<br>\n"; echo "Thread2 : ", $thread2->result,"<br>\n"; echo "Total execution time : ".(microtime(true)-$program_start_time)." seconds<br />";
You should get output similar to this :
Thread1 : 3 seconds have passed. Thread2 : 5 seconds have passed. Total execution time : 5.01491904259 seconds
Note that you can’t pass resource handles to the thread function. Only variables that can be handled by var_export will work.
Using the ThreadManager class
This is functionally equivalent to the previous example, but more elegant.
$program_start_time = microtime(true); //Create a ThreadManager instance with default settings $manager = new ThreadManager; //Create and start two threads. $manager->create_thread('test', array(3)); $manager->create_thread('test', array(5)); /* Wait until all threads are finished. ThreadManager::query() processes all threads and returns the number of threads that are still executing. Check class definition for details.*/ while ($manager->query()); //Output the results foreach ($manager->finished_threads as $id => $thread){ echo "Thread '$id' : ", $thread->result,"<br>\n"; } echo "Total execution time : ".(microtime(true)-$program_start_time)." seconds<br />";
The expected output is something like this :
Thread '_thread_1' : 3 seconds have passed. Thread '_thread_2' : 5 seconds have passed. Total execution time : 5.01980900764 seconds
ThreadManager also has a handy pop_finished_thread() function which returns a finished thread and removes it from managers internal lists.
Final Notes
I’m using the class in my DA recommender project and it seems to be working nicely so far. If you have any questions, feel free to comment ![]()
November 3rd, 2008 at 7:25 pm
I have tried the class on two different server environments both give me an error 404!
Is there anything I need to check?
November 3rd, 2008 at 7:30 pm
@Chris - I take that back- it now gives me a decoding error. Will keep playing.
November 3rd, 2008 at 7:38 pm
@Chris - Got the example code to work. However when running it through my code I get a decoding error
November 3rd, 2008 at 7:51 pm
@Chris - It has been quite a while since I wrote/used this class, so I don’t have any ideas off-hand. Anyway, I’d start looking at the “The response was :” part to see why the response can’t be decoded, and work from there.