Rectangle 27 321

Yes you can do multi-threading in PHP with pthreads

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.

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

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

@Baba, I'm not able to configure and install pthreads on Xampp server. Can you help me with that?

@Baba can you access shared SESSION variables with multi-thread?

That's nice, I have not touched PHP for years and now it's got multithreading capabilities!

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.

multithreading - How can one use multi threading in PHP applications -...

php multithreading
Rectangle 27 316

Yes you can do multi-threading in PHP with pthreads

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.

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

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

@Baba, I'm not able to configure and install pthreads on Xampp server. Can you help me with that?

@Baba can you access shared SESSION variables with multi-thread?

That's nice, I have not touched PHP for years and now it's got multithreading capabilities!

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.

multithreading - How can one use multi threading in PHP applications -...

php multithreading
Rectangle 27 165

From the PHP manual for the pthreads extension:

pthreads is an Object Orientated API that allows user-land multi-threading in PHP. It includes all the tools you need to create multi-threaded applications targeted at the Web or the Console. PHP applications can create, read, write, execute and synchronize with Threads, Workers and Stackables.

As unbelievable as this sounds, it's entirely true. Today, PHP can multi-thread for those wishing to try it.

The first release of PHP4, 22 May 2000, PHP was shipped with a thread safe architecture - a way for it to execute multiple instances of it's interpreter in separate threads in multi-threaded SAPI ( Server API ) environments. Over the last 13 years, the design of this architecture has been maintained and advanced: It has been in production use on the worlds largest websites ever since.

Threading in user land was never a concern for the PHP team, and it remains as such today. You should understand that in the world where PHP does it's business, there's already a defined method of scaling - add hardware. Over the many years PHP has existed, hardware has got cheaper and cheaper and so this became less and less of a concern for the PHP team. While it was getting cheaper, it also got much more powerful; today, our mobile phones and tablets have dual and quad core architectures and plenty of RAM to go with it, our desktops and servers commonly have 8 or 16 cores, 16 and 32 gigabytes of RAM, though we may not always be able to have two within budget and having two desktops is rarely useful for most of us.

Additionally, PHP was written for the non-programmer, it is many hobbyists native tongue. The reason PHP is so easily adopted is because it is an easy language to learn and write. The reason PHP is so reliable today is because of the vast amount of work that goes into it's design, and every single decision made by the PHP group. It's reliability and sheer greatness keep it in the spot light, after all these years; where it's rivals have fallen to time or pressure.

Multi-threaded programming is not easy for most, even with the most coherent and reliable API, there are different things to think about, and many misconceptions. The PHP group do not wish for user land multi-threading to be a core feature, it has never been given serious attention - and rightly so. PHP should not be complex, for everyone.

All things considered, there are still benefits to be had from allowing PHP to utilize it's production ready and tested features to allow a means of making the most out of what we have, when adding more isn't always an option, and for a lot of tasks is never really needed.

pthreads achieves, for those wishing to explore it, an API that does allow a user to multi-thread PHP applications. It's API is very much a work in progress, and designated a beta level of stability and completeness.

It is common knowledge that some of the libraries PHP uses are not thread safe, it should be clear to the programmer that pthreads cannot change this, and does not attempt to try. However, any library that is thread safe is useable, as in any other thread safe setup of the interpreter.

pthreads utilizes Posix Threads ( even in Windows ), what the programmer creates are real threads of execution, but for those threads to be useful, they must be aware of PHP - able to execute user code, share variables and allow a useful means of communication ( synchronization ). So every thread is created with an instance of the interpreter, but by design, it's interpreter is isolated from all other instances of the interpreter - just like multi-threaded Server API environments. pthreads attempts to bridge the gap in a sane and safe way. Many of the concerns of the programmer of threads in C just aren't there for the programmer of pthreads, by design, pthreads is copy on read and copy on write ( RAM is cheap ), so no two instances ever manipulate the same physical data, but they can both affect data in another thread. The fact that PHP may use thread unsafe features in it's core programming is entirely irrelevant, user threads, and it's operations are completely safe.

public function run() {
    ...
    (1) $this->data = $data;
    ...
    (2) $this->other = someOperation($this->data);
    ...
}

(3) echo preg_match($pattern, $replace, $thread->data);

(1) While a read, and write lock are held on the pthreads object data store, data is copied from its original location in memory to the object store. pthreads does not adjust the refcount of the variable, Zend is able to free the original data if there are no further references to it.

(2) The argument to someOperation references the object store, the original data stored, which it itself a copy of the result of (1), is copied again for the engine into a zval container, while this occurs a read lock is held on the object store, the lock is released and the engine can execute the function. When the zval is created, it has a refcount of 0, enabling the engine to free the copy on completion of the operation, because no other references to it exist.

(3) The last argument to preg_match references the data store, a read lock is obtained, the data set in (1) is copied to a zval, again with a refcount of 0. The lock is released, The call to preg_match operates on a copy of data, that is itself a copy of the original data.

No two contexts can physically nor concurrently access the same data from the object store, but writes made in any context with a reference will affect the data read in any context with a reference.

This is shared nothing architecture and the only way to exist is co-exist. Those a bit savvy will see that, there's a lot of copying going on here, and they will wonder if that is a good thing. Quite a lot of copying goes on within a dynamic runtime, that's the dynamics of a dynamic language. pthreads is implemented at the level of the object, because good control can be gained over one object, but methods - the code the programmer executes - have another context, free of locking and copies - the local method scope. The object scope in the case of a pthreads object should be treated as a way to share data among contexts, that is it's purpose. With this in mind you can adopt techniques to avoid locking the object store unless it's necessary, such as passing local scope variables to other methods in a threaded object rather than having them copy from the object store upon execution.

Most of the libraries and extensions available for PHP are thin wrappers around 3rd parties, PHP core functionality to a degree is the same thing. pthreads is not a thin wrapper around Posix Threads; it is a threading API based on Posix Threads. There is no point in implementing Threads in PHP that it's users do not understand or cannot use. There's no reason that a person with no knowledge of what a mutex is or does should not be able to take advantage of all that they have, both in terms of skill, and resources. An object functions like an object, but wherever two contexts would otherwise collide, pthreads provides stability and safety.

Anyone who has worked in java will see the similarities between a pthreads object and threading in java, those same people will have no doubt seen an error called ConcurrentModificationException - as it sounds an error raised by the java runtime if two threads write the same physical data concurrently. I understand why it exists, but it baffles me that with resources as cheap as they are, coupled with the fact the runtime is able to detect the concurrency at the exact and only time that safety could be achieved for the user, that it chooses to throw a possibly fatal error at runtime rather than manage the execution and access to the data.

No such stupid errors will be emitted by pthreads, the API is written to make threading as stable, and compatible as is possible, I believe.

Multi-threading isn't like using a new database, close attention should be paid to every word in the manual and examples shipped with pthreads.

Lastly, from the PHP manual:

pthreads was, and is, an experiment with pretty good results. Any of its limitations or features may change at any time; that is the nature of experimentation. It's limitations - often imposed by the implementation - exist for good reason; the aim of pthreads is to provide a useable solution to multi-tasking in PHP at any level. In the environment which pthreads executes, some restrictions and limitations are necessary in order to provide a stable environment.

@GeoC. I'm not even sure what your point is here, that's just a load of gibberish and you provide no reasons, logical or otherwise, as to why you do not agree with any arguments (of which I cannot really see any in the post).

@Tudor I don't think you really know what you are talking about, so I'm happy to ignore you.

@Tudor - many scientists didn't "see the point" of something new in their branch, or something useful. History has shown that more often than not, people such as yourself are simply wrong, it's a fact. Just like it's a fact that everything you wrote here is, in lack of a better word, feces. I strongly suggest, with everything positive in mind, not to take part in topics that you know nothing about (this being one).

Funny thing. Joe Watkins is the author of pthreads, and still Tudor tries to prove him wrong.

multithreading - Does PHP have threading? - Stack Overflow

php multithreading apache command-line-interface
Rectangle 27 7

Firstly, resource types are officially unsupported by pthreads; a curl handle is a resource, you therefore should not store curl handles in the object scope of pthreads objects, since they might become corrupted.

The easiest way to execute among many threads is to use the built in Pool class provided by pthreads:

The following code demonstrates how to pool a bunch of requests in a few background threads:

<?php
define("LOG", Mutex::create());

function slog($message, $args = []) {
    $args = func_get_args();
    if (($message = array_shift($args))) {
        Mutex::lock(LOG);
        echo vsprintf("{$message}\n", $args);
        Mutex::unlock(LOG);
    }
}

class Request extends Threaded {

    public function __construct($url, $post = []) {
        $this->url = $url;
        $this->post = $post;
    }

    public function run() {
        $curl = curl_init();

        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_URL, $this->url);

        if ($this->post) {
            curl_setopt($curl, CURLOPT_POSTFIELDS, $this->post);
        }

        $response = curl_exec($curl);

        slog("%s returned %d bytes", $this->url, strlen($response));
    }

    public function getURL()      { return $this->url;      }
    public function getPost()     { return $this->post;     }

    protected $url;
    protected $post;
}

$max = 100;
$urls   = [];
while (count($urls) < $max) {
    $urls[] = sprintf(
        "http://www.google.co.uk/?q=%s", 
        md5(mt_rand()*count($urls)));
}

$pool = new Pool(4);

foreach ($urls as $url) {
    $pool->submit(new Request($url));
}

$pool->shutdown();

Mutex::destroy(LOG);
?>

Your specific task requires that you now process the data, you can either write this functionality into a design like the above ... or

Promises suit the nature of the task here:

  • First: Make a request

The following code shows how to use pthreads/promises to make the same request and process responses:

<?php
namespace {
    require_once("vendor/autoload.php");

    use pthreads\PromiseManager;
    use pthreads\Promise;
    use pthreads\Promisable;
    use pthreads\Thenable;

    define("LOG", Mutex::create());

    function slog($message, $args = []) {
        $args = func_get_args();
        if (($message = array_shift($args))) {
            Mutex::lock(LOG);
            echo vsprintf("{$message}\n", $args);
            Mutex::unlock(LOG);
        }
    }

    /* will be used by everything to report errors when they occur */
    trait ErrorManager {
        public function onError(Promisable $promised) {
            slog("Oh noes: %s\n", (string) $promised->getError());
        }
    }

    class Request extends Promisable {
        use ErrorManager;

        public function __construct($url, $post = []) {
            $this->url = $url;
            $this->post = $post;
            $this->done = false;
        }

        public function onFulfill() {
            $curl = curl_init();

            curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($curl, CURLOPT_URL, $this->url);

            if ($this->post) {
                curl_setopt($curl, CURLOPT_POSTFIELDS, $this->post);
            }

            $this->response = curl_exec($curl);
        }

        public function getURL()      { return $this->url;      }
        public function getPost()     { return $this->post;     }
        public function getResponse() { return $this->response; }
        public function setGarbage()  { $this->garbage = true; }
        public function isGarbage()   { return $this->garbage; }

        protected $url;
        protected $post;
        protected $response;
        protected $garbage;
    }

    class Process extends Thenable {
        use ErrorManager;

        public function onFulfilled(Promisable $request) {
            slog("%s returned %d bytes\n",  
                $request->getURL(), strlen($request->getResponse()));
        }
    }

    /* some dummy urls */
    $max = 100;
    $urls   = [];
    while (count($urls) < $max) {
        $urls[] = sprintf(
            "http://www.google.co.uk/?q=%s", 
            md5(mt_rand()*count($urls)));
    }

    /* initialize manager for promises */
    $manager = new PromiseManager(4);

    /* create promises to make and process requests */
    while (@++$id < $max) {
        $promise = new Promise($manager, new Request($urls[$id], []));
        $promise->then(
            new Process($promise));
    }

    /* force the manager to shutdown (fulfilling all promises first) */
    $manager->shutdown();

    /* destroy mutex */
    Mutex::destroy(LOG);
}
?>
{
    "require": {
        "krakjoe/promises": ">=1.0.2"
    }
}

Note that Request has hardly changed, all that has been added is somewhere to hold the response and a means to detect if the objects are garbage.

slog

Many people approach using pthreads as they would approach using a new PDO driver - assume it works like the rest of PHP and that everything will be fine.

Everything might not be fine, and requires research: we are pushing the envelope, in doing so some "restrictions" must be placed upon the architecture of pthreads to maintain stability, this can have some strange side effects.

While pthreads comes with exhaustive documentation which mostly include examples in the PHP manual, I'm not able to attach the following document in the manual, yet.

The following document provides you with an understanding of the internals of pthreads, everyone should read it, it's written for you.

It certainly is a nice answer, I wasnt expecting this much, or a answer so quickly. Thanks a lot Joe.

Always a pleasure ;)

multithreading - PHP cURL - thread safe? - Stack Overflow

php multithreading curl thread-safety
Rectangle 27 7

<?php
/**
* Normal worker
*/
class PooledWorker extends Worker {
    public function run(){}
}


/**
* Don't descend from pthreads, normal objects should be used for pools
*/
class Pool {
    protected $size;
    protected $workers;

    /**
    * Construct a worker pool of the given size
    * @param integer $size
    */  
    public function __construct($size) {
        $this->size = $size;
    }

    /**
    * Start worker threads
    */
    public function start() {
        while (@$worker++ < $this->size) {
            $this->workers[$worker] = new PooledWorker();
            $this->workers[$worker]->start();
        }
        return count($this->workers);
    }

    /**
    * Submit a task to pool
    */
    public function submit(Stackable $task) {
        $this->workers[array_rand($this->workers)]
            ->stack($task);
        return $task;
    }

    /**
    * Shutdown worker threads
    */
    public function shutdown() {
        foreach ($this->workers as $worker)
            $worker->shutdown();
    }
}

class StageTwo extends Stackable {
    /**
    * Construct StageTwo from a part of StageOne data
    * @param int $data
    */
    public function __construct($data) {
        $this->data = $data;
    }

    public function run(){
        printf(
            "Thread %lu got data: %d\n", 
            $this->worker->getThreadId(), $this->data);
    }
}

class StageOne extends Stackable {
    protected $done;

    /**
    * Construct StageOne with suitable storage for data
    * @param StagingData $data
    */
    public function __construct(StagingData $data) {
        $this->data = $data;
    }

    public function run() {
        /* create dummy data array */
        while (@$i++ < 100) {
            $this->data[] = mt_rand(
                20, 1000);
        }
        $this->done = true;
    }
}

/**
* StagingData to hold data from StageOne
*/
class StagingData extends Stackable {
    public function run() {}
}

/* stage and data reference arrays */
$one = [];
$two = [];
$data = [];

$pool = new Pool(8);
$pool->start();

/* construct stage one */
while (count($one) < 10) {
    $staging = new StagingData();
    /* maintain reference counts by storing return value in normal array in local scope */
    $one[] = $pool
        ->submit(new StageOne($staging));
    /* maintain reference counts */
    $data[] = $staging;
}

/* construct stage two */
while (count($one)) {

    /* find completed StageOne objects */
    foreach ($one as $id => $job) {
        /* if done is set, the data from this StageOne can be used */
        if ($job->done) {
            /* use each element of data to create new tasks for StageTwo */
            foreach ($job->data as $chunk) {
                /* submit stage two */
                $two[] = $pool
                    ->submit(new StageTwo($chunk));
            }

            /* no longer required */
            unset($one[$id]);
        }
    }

    /* in the real world, it is unecessary to keep polling the array */
    /* you probably have some work you want to do ... do it :) */
    if (count($one)) {
        /* everyone likes sleep ... */
        usleep(1000000);
    }
}

/* all tasks stacked, the pool can be shutdown */
$pool->shutdown();
?>
Thread 140012266239744 got data: 612
Thread 140012275222272 got data: 267
Thread 140012257257216 got data: 971
Thread 140012033140480 got data: 881
Thread 140012257257216 got data: 1000
Thread 140012016355072 got data: 261
Thread 140012257257216 got data: 510
Thread 140012016355072 got data: 148
Thread 140012016355072 got data: 501
Thread 140012257257216 got data: 767
Thread 140012024747776 got data: 504
Thread 140012033140480 got data: 401
Thread 140012275222272 got data: 20
<-- trimmed from 1000 lines -->
Thread 140012041533184 got data: 285
Thread 140012275222272 got data: 811
Thread 140012041533184 got data: 436
Thread 140012257257216 got data: 977
Thread 140012033140480 got data: 830
Thread 140012275222272 got data: 554
Thread 140012024747776 got data: 704
Thread 140012033140480 got data: 50
Thread 140012257257216 got data: 794
Thread 140012024747776 got data: 724
Thread 140012033140480 got data: 624
Thread 140012266239744 got data: 756
Thread 140012284204800 got data: 997
Thread 140012266239744 got data: 708
Thread 140012266239744 got data: 981

Because you want to use one pool, you have little choice but to create the tasks in the main context where the pool is created. I can imagine other solutions but you specifically asked for this kind of solution.

Depending on the hardware I had at my disposal, and the nature of the tasks and data to be processed, I might well have multiple [small] pools of threads, one for each worker, this would allow StageOne to create StageTwo objects in the Worker context that is executing them, it might be something to consider.

Thank you for your answer, and very interesting reading. But this is not exactly what I need. When Stackables from stage 1 end, data has to be processed, and new Stackables have to be created. For each Stackable from stage 1, there can be like 1000 Stackables in stage 2

I'm the second busiest guy on the face of the earth today, let me come back to it tomorrow and do you a better example ...

Sounds like what I'm trying to do: I want the task or worker to decide what the next task to be executed on the bit of data is. I ultimately decided on a queueing thingy that specifies what kind of task to perform on which bit of data, the main loop of the program picking up tasks from the queue and submitting them to a pool. Still haven't worked all the bugs out, though - the most important one being: it doesn't work.

multithreading - PHP Thread Pool - Stack Overflow

php multithreading threadpool
Rectangle 27 3

After google-ing one day for a technique to get the same behavior you are describing here I come up with an easy solution for my project.

A bit of important theory: - session_start () and a set like $_SESSION["x"] = "y" will always lock the session file.

- A - process.php - running through an ajax call

The main problem is/was, that even if you set a $_SESSION inside a process that is being run through an AJAX it will always have to wait the for the session file to get unlocked and it will result into a sync between the two processes (A. + B.) - both finishing at the same time!

So, the easiest way to fix this matter and get a good result is by using session_write_close() after each set. E.g.:

%_SESSION["A"] = "B";
$_SESSION["x"] = "y";
session_write_close();

PS: Best approach is to have a customed set of functions to handle the sessions.

ajax - Getting real time feedback from a server process [in PHP] - Sta...

php ajax pthreads fork proc
Rectangle 27 6

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

multithreading - How can one use multi threading in PHP applications -...

php multithreading
Rectangle 27 6

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

multithreading - How can one use multi threading in PHP applications -...

php multithreading
Rectangle 27 0

I really think you should look at pthreads which provides multi-threading that is compatible with PHP based on Posix Threads.

Simple as

class AsyncOperation extends Thread {
    public function __construct($arg) {
        $this->arg = $arg;
    }
    public function run() {
        if ($this->arg) {
            echo 'child ' . $this->arg . ' starts' . "\n";
            $wait_time = mt_rand(1, 4);
            echo 'sleeping for ' . $wait_time . "\n";
            sleep($wait_time);
            echo 'child ' . $this->arg . ' ends' . "\n";
        }
    }
}
$t = microtime(true);
$g = array();
foreach(range("A","D") as $i) {
    $g[] = new AsyncOperation($i);
}
foreach ( $g as $t ) {
    $t->start();
}
child B starts
sleeping for 3
child B ends
child C starts
sleeping for 3
child C ends
child A starts
sleeping for 4
child A ends
child D starts
sleeping for 4
child D ends

looks good. what are the advantages?

ok, better organized code, OO, synchronization is already implemented, I'm convinced. Thanks

fock
100% POSIX Threads
PHP

why is it better than forking? Problem is i need thread-safe enabled and I'm running php-fpm with nginx so it's disabled atm since it wasn't necessary, so I'll need to reinstall php. before that i need to check if it has performance issues under the current setup

forking creates new process which has a fair amount of overhead though it provides greater isolation

php - Calling rand/mt_rand on forked children yields identical results...

php random fork
Rectangle 27 0

You should not write the standard output from threads in SAPI mode, you will experience unexpected behaviour and errors, that cannot be controlled, there are too many environments and SAPI's to cover it well, so it is not covered at all, don't do it.

Even in CLI mode output of complex code will be garbled, to work around this, you can define a protected method in any object you pass to all contexts that takes care of writing standard output, if the method is protected and the object is a pthreads one, only one context will be able to write standard output at a time ... the same object could be used in the SAPI environment by swapping standard output for a logging database ...

do you have any example code where i can see how exactly to do that? i mean if there are good practice topic i would like to read them.

stackoverflow.com/a/14565559/1658631 contains an example where output to console is synchronized using protected methods, there are many examples included in the distribution of pthreads covering a wide variety of topics.

@EmrahMehmedov you can also sink the result to stackable and use print_r to see each step how it was processed .... Nothing is simulated

pthreads for PHP not executing parallel threads in Apache - Stack Over...

php pthreads pthreads-win32
Rectangle 27 0

Wamp server has a separate php.ini config file for the browser and for the cli.

To use the pthreads module in the browser with WAMP Server you need to copy the "pthreadVC2.dll" into the apache "bin" directory also.

You should now have he "pthreadVC2.dll" in both of these folders (if installed in default location):

C:\wamp\bin\php\php[x.x.xx]\bin
C:\wamp\bin\apache\apache[x.x.x]\bin

You will also need to update the php.ini file within the php bin directory AND the apache bin directory to include:

extension=php_pthreads.dll

This now means you can use pthreads in the browser and in the cli with wamp server

multithreading - How to get pthreads working in PHP? - Stack Overflow

php multithreading pthreads
Rectangle 27 0

After a bit more research, it seems that the session variable is not thread safe and in fact locks entirely until the session is closed. I had to remove references to the session and simply pass the needed information to the function or reinitialize it.

Luckily there isn't a performance downfall of the property manager being created.

php - Accessing the $_SESSION variable from pthreads - Stack Overflow

php multithreading session pthreads scoping
Rectangle 27 0

Just as you do with any other object in PHP [or any lanugage], you should pass parameters to constructors to set members.

class My extends Thread {

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

    public function run() {
        printf(
            "Hello %s\n", $this->greet);
    }
}

$my = new My("World");
$my->start();
$my->join();

No special action need be taken for scalars and simple data that you are just passing around, however should you intend to manipulate an object in multiple threads, the object's class should descend from pthreads:

class Greeting extends Threaded {

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

    public function fetch() {
        return $this->greet;
    }

    protected $greet;
}

class My extends Thread {

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

    public function run() {
        printf(
            "Hello %s\n", $this->greet->fetch());
    }
}

$greeting = new Greeting("World");
$my = new My($greeting);
$my->start();
$my->join();

php - Passing parameter from main thread to new thread - Stack Overflo...

php multithreading pthreads parameter-passing
Rectangle 27 0

Promises in PHP with pthreads

Everyone understands the concept of a stack when we talk about Javascript; this is how animations, and lots of the other fancy stuff we do on the web, are run.

In effect, my effort to write promises for pthreads is an attempt to make threading as simple as a Javascript stack, which everyone understands.

It just so happens that this very thing suits your needs almost perfectly:

$manager = new PromiseManager();
$promise = 
    new Promise($manager, new CalculateTheMeaningOfLife());
$promise
    ->then(
        new AddTwo($promise))
    ->then(
        new PrintMeaning($promise));

$manager->shutdown();

You do have to use objects and not closures (limitation of Zend), but it is the promises pattern and it is fully async and managed.

This is PHP, so can be installed with composer ... if you like that sort of thing ...

Is there a way to share somehow variables (something non serializable) between the tasks?

You can see that the AddTwo and PrintMeaning classes both accept the ooriginal promise object and work with it's members.

PHP - parallel task runner - Stack Overflow

php parallel-processing
Rectangle 27 0

The Zend memory manager, with the help of TSRM, are purposefully built to prohibit contexts from sharing data; no context can allocate or free anything in another thread. This is referred to as a shared nothing architecture and it does exactly what it says on the tin. The job of pthreads is to breach that barrier in a safe, sane manner.

Obviously, none of the extensions bundled with PHP are aware of pthreads, nor do we want them to be, for they would just become vastly complicated. When it comes to objects provided by other extensions, pthreads makes the assumption that the safest thing to do is serialize the object for storage. Some objects prohibit this from occurring, SimpleXML descendants are one such group of objects.

Strings, floats, integers and objects that descend from pthreads are not serialized. Objects that descend from pthreads hi-jack the serialization API, storing the physical addressof the object, avoiding serialization by directly accessing the thread safe structure representing the object in userland.

The proper solution is to wrap the data you wish to share in an object descended from pthreads:

<?php
class Config extends Stackable {
    /**
    * Constructs thread safe, sharable configuration from complex XML
    * @param mixed $xml         SimpleXMLElement or location of XML file
    * @param array &$objects    reference store
    */
    public function __construct($xml, &$objects) {
        if ($xml instanceof SimpleXMLElement) {
            foreach ($xml as $key => $value)
                $this[$key] = (string) $value;
        } else {
            foreach (simplexml_load_string(
                        $xml) as $key => $value) {
                if ($value->children()) {
                    $this[$key] = new Config($value, $objects);
                } else $this[$key] = (string) $value;
            }
        }

        /* maintain object references */
        $objects[] = $this;
    }

    public function run() {}
}

class Test extends Thread {
    protected $config;

    /**
    * Constructs thread using pre-constructed thread safe, shared configuration object
    * @param Config $config
    */
    public function __construct(Config $config) {
        $this->config = $config;
    }

    public function run() {
        /* iterate over configuration */
        printf("%d settings:\n", count($this->config));
        foreach ($this->config as $key => $data) {
            if (count($data) > 1) {
                printf( 
                    "\t%s, %d settings:\n", $key, count($data));
                foreach ($data as $name => $value) {
                    printf("\t\t%s = %s\n", $name, $value);
                }
            } else printf("\t%s = %s\n", $key, $data);
        }
        printf("\n");

        printf(
            "Host: %s:%d\n", 
            $this->config->ip, 
            $this->config->port);

        printf(
            "MySQL: %s@%s?database=%s&password=%s&table=%s\n", 
            $this->config->mysql->username,
            $this->config->mysql->host,
            $this->config->mysql->dbname,
            $this->config->mysql->password,
            $this->config->mysql->table);
    }
}

/* Example XML */
$xml = <<<XML
<server>
    <port>6112</port>
    <ip>some.ip</ip>
    <mysql>
        <host>127.0.0.1</host>
        <username>root</username>
        <dbname>somedb</dbname> 
        <password>somepass</password>
        <table>sometable</table>
    </mysql>
</server>
XML;

/* Object reference storage */
$objects = [];
$config = new Config($xml, $objects);

$thread = new Test($config);
$thread->start();

$thread->join();
?>
3 settings:
        port = 6112
        ip = some.ip
        mysql, 5 settings:
                host = 127.0.0.1
                username = root
                dbname = somedb
                password = somepass
                table = sometable

Host: some.ip:6112
MySQL: root@127.0.0.1?database=somedb&password=somepass&table=sometable

The example provided uses the [format] XML you provided in the question, it takes that XML and creates a thread safe representation of it, which will never be serialized.

The logic in the constructor of Config wholly depends upon the format of the XML you are using.

You can pass that Config object to as many threads as you wish, all of them can read/write properties, and execute it's methods.

All data that you intend to share should be managed in this way, what you want to take away from this is not that you should work around the exception and try to store serial data, but rather that you should create suitable containers for your data that actually support, properly, multi-threading.

This answer isn't entirely clear to me: you say "an object descended from pthreads", but the example actually descends from a class Stackable, which apparently has some ArrayAccess magic, since you then do $this[$key]. A link to full documentation on that might be good. It's also not clear if this could be used to transport the SimpleXML object itself, since here you're just extracting strings from it, which won't work with the XML provided by the OP, and would serialize fine without any special tricks.

It would serialize, but not be thread safe, so not shared. php.net/intro.pthreads

So, the Stackable allows the Config items to be writeable, but doesn't actually solve the OP's problem of passing a SimpleXML object around? The actual workaround for the SimpleXML serialization issue seems to be this line: $this[$key] = (string) $value;

That is intractable: lxr.php.net/xref/PHP_5_4/ext/simplexml/simplexml.c#2636 The object cannot be stored in it's current form, I provided the correct way to store the data so that it can be shared.

Please correct me if I'm wrong, but it seems that making sokserv extend Stackable, while probably a good idea, won't actually solve the OP's problem. The solution to the OP's problem is to convert the SimpleXML object into a form which is safe for serialization. Your code does this in a crude loop which will not work for the example XML in the question, and doesn't comment that this is a key part of the solution.

xml - PHP Threading SimpleXMLElement - Stack Overflow

php xml multithreading pthreads simplexml
Rectangle 27 0

You are requesting all the data in one thread, here's a better approach:

<?php

class WebRequest extends Stackable {
    public $request_url;
    public $response_body;

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

    public function run(){
        $this->response_body = file_get_contents(
            $this->request_url);
    }
}

class WebWorker extends Worker {
    public function run(){}
}

$list = array(
    new WebRequest("http://google.com"),
    new WebRequest("http://www.php.net")
);

$max = 8;
$threads = array();
$start = microtime(true);

/* start some workers */
while (@$thread++<$max) {
    $threads[$thread] = new WebWorker();
    $threads[$thread]->start();
}

/* stack the jobs onto workers */
foreach ($list as $job) {
    $threads[array_rand($threads)]->stack(
        $job);
}

/* wait for completion */
foreach ($threads as $thread) {
    $thread->shutdown();
}

$time = microtime(true) - $start;

/* tell you all about it */
printf("Fetched %d responses in %.3f seconds\n", count($list), $time);
$length = 0;
foreach ($list as $listed) {
    $length += strlen($listed["response_body"]);
}
printf("Total of %d bytes\n", $length);
?>

This uses multiple workers, which you can adjust by changing $max. There's not much point in creating 1000 threads if you have 1000 requests to process.

Then which is faster Pthreads or Curl_MULTI?

multithreading - PHP testing between pthreads and curl - Stack Overflo...

php multithreading curl
Rectangle 27 0

You have to build pthreads for your centos op system. After that you can use it as extension...

Use the following tutorials

Or simply read the manual...

Configuring PHP for pthreads - Stack Overflow

php pthreads centos
Rectangle 27 0

By default Threads are not implemented in PHP, and according to your phpinfo it does not seem to be loaded. Check out the PHP manual on how to set-up/configure the module.

multithreading - PHP pthreads: Fatal error: Class 'Thread' not found -...

php multithreading pthreads
Rectangle 27 0

#!/bin/bash

mkdir -p /etc/php7
mkdir -p /etc/php7/cli

git clone https://github.com/php/php-src.git -b PHP-7.0.17 --depth=1
cd php-src/ext
git clone https://github.com/krakjoe/pthreads -b master pthreads

cd ..

./buildconf --force

./configure --prefix=/etc/php7 --with-bz2 --with-zlib --enable-zip --disable-cgi \
   --enable-soap --enable-intl --with-mcrypt --with-openssl --with-readline --with-curl \
   --enable-ftp --enable-mysqlnd --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd \
   --enable-sockets --enable-pcntl --with-pspell --with-enchant --with-gettext \
   --with-gd --enable-exif --with-jpeg-dir --with-png-dir --with-freetype-dir --with-xsl \
   --enable-bcmath --enable-mbstring --enable-calendar --enable-simplexml --enable-json \
   --enable-hash --enable-session --enable-xml --enable-wddx --enable-opcache \
   --with-pcre-regex --with-config-file-path=/etc/php7/cli \
   --with-config-file-scan-dir=/etc/php7/etc --enable-cli --enable-maintainer-zts \
   --with-tsrm-pthreads --enable-debug --enable-fpm \
   --with-fpm-user=www-data --with-fpm-group=www-data

make && make install

chmod o+x /etc/php7/bin/phpize
chmod o+x /etc/php7/bin/php-config

cd ext/pthreads*
/etc/php7/bin/phpize

./configure --prefix=/etc/php7 --with-libdir=/lib/x86_64-linux-gnu --enable-pthreads=shared --with-php-config=/etc/php7/bin/php-config

make && make install

cd ../../
cp -r php.ini-development /etc/php7/cli/php.ini

cp php.ini-development /etc/php7/cli/php-cli.ini
echo "extension=pthreads.so" > /etc/php7/cli/php-cli.ini
echo "zend_extension=opcache.so" >> /etc/php7/cli/php.ini

ln --symbolic /etc/php7/bin/php /usr/bin/php

export USE_ZEND_ALLOC=0
docker pull buildok/zts

Thanks, it's seem to work fine as is. i will test with php 7.1 branch. Do you now if it's possible to have a php apache module when compiling from sources ?

@Khorwin I use nginx usually. If you need to apache-mod support, you can add a parameter in ./configure --with-apxs2=/usr/local/apache2/bin/apxs look detail about this php.net/manual/en/install.unix.apache2.php

@Khorwin Pthreads cannot be used in a web server environment, so you will not be able to have pthreads enabled if PHP is loaded as an Apache module.

@buildok You have debug mode enabled - that probably isn't desirable for most people here. Also, the TSRM defaults to POSIX threads, so there's no real need to specify it.

pthreads - How to compile PHP 7.1 with ZTS - Stack Overflow

php pthreads ubuntu-16.04 php-7.1 zts
Rectangle 27 0

You cannot; Zend Thread Safety is a compile time, only, option.

Either find a thread safe package being maintained by someone else, or build yourself.

pthreads - Enable ZTS on PHP without compiling - Stack Overflow

php pthreads