How should a PHP thread store its data? - php

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

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.

Related

Background Processing using pthread in php

I am trying to implement muti-threading in php using pthread to send emails. The basic idea here is to send email as a background job so that users dose not have to wait for the task to finish.
I have a service that users PHPMailer to send emails and its working fine. I am using the following code to muti thread
class ThreadWorkerService extends \Thread {
private $_runMethod;
private $_vars;
private $_autoloderPath;
function __construct($vars) {
$this->_vars = $vars;
$reflector = new \ReflectionClass('Composer\Autoload\ClassLoader');
$dir = dirname($reflector->getFileName());
$this->_autoloderPath = $dir . "/../autoload.php";
}
public function setRunMethod($method) {
$this->_runMethod = $method;
}
public function run() {
if (!class_exists('Composer\Autoload\ClassLoader')) {
if (file_exists($this->_autoloderPath )) {
require_once( $this->_autoloderPath );
} else {
echo "autoloder not found";
}
}
$method = $this->_runMethod;
$results = $method($this->_vars);
}
}
$storage = new \Threaded();
$storage['emails'] = $emailArray;
$storage['config'] = $config;
$threadWorker = new ThreadWorkerService($storage);
$threadWorker->setRunMethod(function ($vars) {
$emailArray = $vars['emails'];
$config = $vars['config'];
$emailService = new \MyServices\EmailService($config);
$emailService->sendAllEmails(true, $emailArray);
}
});
$threadWorker->start(PTHREADS_INHERIT_CONSTANTS);
The issue here is that the tread dose not execute if i don't use
$threadWorker->join();
which eliminates the whole purpose of muti-treading in this scenario, what I want to know is, if it is possible to keep the child thread alive even when the parent process is complete. I have even tried detaching the thread with no luck.
I am familiar with on how to do it with messaging services like RabbitMQ, but I want to keep the application independent.
The issue here is that the thread does not execute if I don't use: $threadWorker->join();, which eliminates the whole purpose of multi-threading in this scenario.
It is likely the case that the main thread's stdout, or some other dependency, is being closed or destroyed before the thread gets to execute.
Taken from the PHP manual for Thread:
Warning: Relying on the engine to determine when a Thread should join may cause undesirable behaviour; the programmer should be explicit, where possible.
You don't need to join immediately, but it is advisable to join explicitly.
The following example code illustrates the problem:
<?php
class Test extends Thread {
public function run() {
sleep(1);
echo "Thread\n";
}
}
$test = new Test();
$test->start();
echo "End\n";
?>
CPU speed permitting, the main context will begin to destroy the Thread before the Thread gets to echo.
You will normally get the output:
End
Thread
However, if the code is more complex, for example, code that manipulates dependencies set in the constructor of the Thread, those dependencies might have already been destroyed by the time they are used.
A last ditch attempt to join implicitly is made (in the most recent versions of pthreads), but you cannot rely on destruction order being compatible with your expectations, and so explicit join is preferable.
Noteworthy: The most recent versions of pthreads (PHP7+) prohibit execution anywhere but CLI; If this is code that is being executed at the frontend of a web server, you will need to rethink your architecture.

php pthreads and using a class to store data that is shared between threads

I'm fairly new to PHP, and now very new to pthreads.
I'm using the latest PHP7 RC6 build, with pthreads built from git/src to get the latest (and tried the 'official' v3.0.8 one), on Ubuntu 3.13.0-66-generic
I'm trying to write a threaded solution to read in data from a socket and process it. I'm using threading to try to maximize my performance, mainly due to the fact I'm doing operations like http requests (to AWS DynamoDB and other services) and such that are waiting for responses from external systems, and therefore I can benefit from threading.
The real code I have is more complicated than this is. This is a simple example to show my problem.
What I am trying to do is to 'cache' certain information in an 'array' that I get from a database (AWS DynamoDB) so that I can get better performance. I need each thread to be able to use/access and modify this 'global' cache, and with multiple 'records' in the cache.
I had great success with testing and simply storing a string in this way, but now I'm doing it for real, I need to store more complicated data, and I decided to use a little class (cacheRecord) for each record, instead of a simple string of data. But the problem is that when I try to assign a value back to a class member, it seems to not want to 'save', back to the array.
I managed to get it to work by copying the whole 'class' to a tmp variable, modifying that, and then saving back the whole class to the array, but that seems like an overhead of code, and also I would need to wrap it in a ->synchronized to keep integrity between threads.
Is this the only way to do it correctly, with copying it to a tmp and copying it back and using 'synchronized', or am I doing something else wrong/stupid?
Experimenting with it, I made the cacheRecord class 'extends Threaded'. This made the single assign of the member work fine, but this then made it immutable, and I couldn't unset/delete that record in the cache later.
Code to show what I mean:
<?php
class cacheRecord {
public $currentPos;
public $currentRoom;
public $someOtherData;
}
class cache extends Threaded {
public function run() {}
}
class socketThread extends Thread {
public function __construct($myCache) {
$this->cacheData = $myCache;
}
public function run() {
// This will be in a loop, waiting for sockets, and then responding to them, indefinitely.
// At some point, add a record to the cache
$c = new cacheRecord;
$c->currentPos = '1,2,4';
$c->currentRoom = '2';
$this->cacheData['record1'] = $c;
var_dump($this);
// Later on, update the cache record, but this doesnt work
$this->cacheData['record1']->currentRoom = '3';
var_dump($this);
// However this does work, but is this the correct way? Seems like more code to execute, than a simple assign, and obviously, I would need to use synchronized to keep integrity, which would further slow it down.
$tmp = $this->cacheData['record1'];
$tmp->currentRoom = '3';
$this->cacheData['record1'] = $tmp;
var_dump($this);
// Later on some more, remove the record
unset($this->cacheData['record1']);
var_dump($this);
// Also will be using ->synchronized to enforce integrity of certain other operations
// Just an example of how I might use it
/*
$this->cacheData->synchronized(function() {
if ($this->cacheData['record1']->currentRoom == '3') {
$this->cacheData['record1']->Pos = '0,0,0'; // Obviously this wont work as above.
$this->cacheData['record1']->currentRoom = '4';
}
});
*/
}
}
// Main
$myCache = new cache;
for ($th=0;$th<1;$th++) { // Just 1 thread for testing
$socketThreads[$th] = new socketThread($myCache);
$socketThreads[$th]->start();
}
extends \Threaded is the way to go.
However, "anything" in the cache should be extended from this, not only the cache itsef.
It is explained somewhere in the manuals (sorry dont remember exactly where) than only volatile (aka threaded) object will not me immutable.
So if your class cacheRecord is not extended from threaded, it will be immutable, even into another threaded structure.
threaded makes inner attributes array automatically volatile (so thread-usable), but not object if they are not extended from threaded.
Try extending cacheRecord from threaded and tell me if it works.
Phil+

PHP pthreads : impossible to create object correctly in constructor

I am new to threads in PHP and I have a problem with classes extending Thread.
I need to store an object given in the constructor parameters into a SplObjectStorage but I am unable to do it.
The SplObjectStorage object remains empty even after attaching an object.
When I remove the "extends Thread", it works correctly but I need to use threads.
Here is an example :
<?php
class MyClass extends Thread
{
protected $variable;
public function __constructor($object)
{
$this->variable = new \SplObjectStorage;
$this->variable->attach($object);
$storage = new \SplObjectStorage;
$storage->attach($object);
var_dump($this->variable); // shows empty SplObjectStorage
var_dump($storage); // shows SplObjectStorage with $object inside it
var_dump($object); // shows the object
}
public function run() {}
}
Have you got an idea ?
I want $this->variable to be like $storage after the attachment.
Thank you.
The author of PHP pthreads has already answered this a couple of times on Stack Overflow.
Objects not extending Threaded aren't considered thread-safe and are therefore serialized/copied by pthreads.
You can make "thread-safe" classes from normal classes at runtime with Threaded::extend(). But be very careful. The underlying implementation might not be thread-safe. (SplObjectStorage fails here, by the way.)
I'm afraid you have to create/use SplObjectStorage thread-locally.
protected static $variable; // will be NULL in thread's context
OK, I must correct myself here a bit. I'm still in the process of learning pthreads myself.
Actually, you can use normal objects as class properties. But you have to access them somewhat unconventionally (and it's not very fast):
$storage = new \SplObjectStorage;
$storage->attach($object);
$this->variable = $storage; // object gets serialized
$storage = $this->variable // object gets unserialized
// work with $storage normally...
// ...and write it back:
$this->variable = $storage
Also note that you're working on copies when using the technique above. Other threads won't see changes until you write the object back. You might want to use Threaded::synchronized() here.
You always have to keep in mind that objects and arrays will be serialized when setting a Threaded property and unserialized when getting that property.
PS: Working with strings as Threaded properties is also very slow...

Why are these pthreads segmentation faults possible?

In this introduction to pthreads I read that:
When the programmer calls Thread::start, a new thread is created, a PHP interpreter context is initialized and then (safely) manipulated to mirror the context that made the call to ::start.
And later in the text the segmentation fault problem is addressed. This example of a segmentation fault is given:
class W extends Worker {
public function run(){}
}
class S extends Stackable {
public function run(){}
}
/* 1 */
$w = new W();
/* 2 */
$j = array(
new S(), new S(), new S()
);
/* 3 */
foreach ($j as $job)
$w->stack($job);
/* 4 */
$j = array();
$w->start();
$w->shutdown();
The above example will always segfault; steps 1-3 are perfectly normal, but before the Worker is started the stacked objects are deleted, resulting in a segfault when the Worker is allowed to start.
The questions are:
Is the whole context that starts the new thread copied into the new thread when start() is called, or only at the time the interpreter sees a reference to a variable of the old context? In other words, is it enough to keep refcounts > 0 until start() is called?
Shouldn't references to the Stackable array enties be stored inside the Worker object so that the refcount of them after overwriting $j is still 1 and no segfault could occur?
1) The whole context is copied when the thread is started. You have to keep refcounts > 0 until the stacked objects are actually executed by the worker thread.
2) The reference counting built into variables in PHP was never prepared for multi-threading, many api functions decrement and increment refcounts and there is no opportunity to synchronize (lock). For that reason, you are responsible for maintaining a reference to any object that is destined to be executed by another thread.
These rather annoying, but unavoidable, facts can be side stepped by using the Pool abstraction provided with pthreads. It maintains references for you in the proper way.
http://php.net/Pool

Shared Resources Between PHP Children 'Threads'

I have a PHP script that spawns two child processes (pcntl_fork()) which each call methods of a single class instance (the names of the classes and methods and their functionality are simplified for easier explanation).
class Main_Class
{
public $array = array();
public function push($value) {
$this->array[] = $value;
}
public function pop() {
return array_shift($this->array);
}
}
$pc = new Thread_Creator();
$main_class = new Main_Class();
$pc->fork(array($main_class, 'push'), 1);
$pc->fork(array($main_class, 'pop'), 1);
The fork method is executed like so: call_user_func($main_class, 'pushToArray')
I intended these processes to share the same resources (one process pushes information into a public class variable of type array while one pops from it). Currently, both process run concurrently but only one seems to have access to the class variable $array.
Are shared resources possible between separate PIDs?
Any suggestions for solving this issue? (I would like to keep the processes separate as I intend to run them at different intervals)
If you need any more info or code snippets let me know
I was able to use shared memory to accomplish the task. For those interested, take a look here:
http://onlamp.com/pub/a/php/2004/05/13/shared_memory.html?page=2
http://www.php.net/manual/en/book.sem.php

Categories