3

So I have been googling and reading up and down the internet about PHP pthreads3 and how they are supposed to store data. (Or rather, how they are not) It seems to me that the only way for a thread to store its data properly is to create a new Threaded object and send it to the thread. The thread can then use this Threaded object to store nearly any data.

My question, and biggest issue with grasping PHP threads: Is it possible to have the thread create its own storage objects when it wants? I have no idea how or why, since all the answer I've found on this tell a vague, elaborate and confusing "maybe, but no", mostly related to poor performance and memory issues/safety. This seems like it should be possible, somehow:

class someFantasticThread extends Thread {
    public $someData;

    function run(){
        while(true){
            //Create a fresh storage for the new data this iteration
            $this->someData = new SomeCoolStorage(); // Can this work somehow without all the issues?
            $this->someData[] = 'amazingdata'; // Do something amazing and store the new results in $someData
            $this->someData[] = new SomeCoolStorage(); // This would also be desireable, if it can somehow be done
            //don't mind the obvious loop issues. Imagine this is a well formed loop
        }
    }
}

class SomeCoolStorage extends Threaded{}

// Start the thread
$threadObj = new someFantasticThread();
$threadObj->start();
while(true){
    // at some point, retrieve the data and do something useful with the contained results
    // doSomethingAwesome($threadObj->someData);
}
JohnF
  • 98
  • 9
  • What environment do you plan on using this code in? Keep in mind that PHP threads are incompatible with a web server environment -- they are almost never appropriate to use. –  Jun 26 '17 at 22:42
  • I am willing to use this in any environment that is able perform what I'm asking: letting a thread create its own empty objects in a proper manner. And no, this is not for a web server. – JohnF Jun 26 '17 at 23:10
  • But what are you trying to use threads for in the first place? –  Jun 26 '17 at 23:11
  • 1
    I work with PHP, but have not touched threading yet. Since PHP threads exist, and have existed for some time, I'm assuming it has some kind of purpose that has to do with utilizing the hardware better to do more tasks in a shorter amount of time. This certainly sounds like a skill I would like to learn, which is why I'm asking about the thing that I cannot seem to find a good answer for atm. – JohnF Jun 26 '17 at 23:20

1 Answers1

6

It seems to me that the only way for a thread to store its data properly is to create a new Threaded object and send it to the thread.

Yes, that is one way to do it.

Is it possible to have the thread create its own storage objects when it wants?

Yes, but only if you manipulate it within that thread (or any child threads it may spawn).

One of the fundamental things to understand when using threads in PHP is that objects of a Threaded class are tied to the context in which they are created. This means that if you create a Threaded object in the main thread, pass this object into a spawned child thread, and then join that spawned child thread, then you may continue to use that Threaded object as normal.

Example 1 (constructor injection):

<?php

$store = new Threaded(); // created in the main thread

$thread = new class($store) extends Thread {
    public $store;

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

    public function run()
    {
        $this->store[] = 1;
        $this->store[] = 2;
    }
};

$thread->start() && $thread->join();

print_r($store); // continue using it in the main thread

This will output:

Threaded Object
(
    [0] => 1
    [1] => 2
)

In the example above, we could also have created the Threaded object inside of the constructor, and then performed a var_dump($thread->store); at the end of the script. This works because the Threaded object is still being created in the outermost scope in which it is needed, and thus it is not tied to the scope of any child threads that may have already been destroyed. (The only part of a Thread in PHP that is executed in a separate thread is the Thread::run method.)

Similar to the above example, we could also have used setter injection. (Though, again, just so long as the setter is being called by the thread in the outer most scope in which the Threaded object will be used.)

The problem that many developers who are new to threading in PHP seem to encounter, is when they create a Threaded object from inside of a new thread, and then expect to be able to use that Threaded object when they have joined that same thread.

Example:

<?php

$thread = new class() extends Thread {
    public $store;

    public function run()
    {
        $this->store = new Threaded(); // created inside of the child thread
        $this->store[] = 1;
        $this->store[] = 2;
    }
};

$thread->start() && $thread->join();

print_r($thread->store); // attempt to use it in the outer context (the main thread)

This will output:

RuntimeException: pthreads detected an attempt to connect to an object which has already been destroyed in %s:%d

This is because the Threaded object in $thread->store has been destroyed when joining the spawned child thread. This problem can be far more subtle, too. For example, creating new arrays inside of Threaded objects will automatically cast them to Volatile objects (which are also Threaded objects).

This means that the following example will not work either:

<?php

$thread = new class() extends Thread {
    public $store;

    public function run()
    {
        $this->store = [];
        $this->store[] = 1;
        $this->store[] = 2;
    }
};

$thread->start() && $thread->join();

print_r($thread->store);

Output:

RuntimeException: pthreads detected an attempt to connect to an object which has already been destroyed in %s:%d

To come back to your example code, what you're doing is absolutely fine, but only so long as you do not attempt to use $this->someData outside of that child thread.

tpunt
  • 2,552
  • 1
  • 12
  • 18