I've found a PECL pthread Thread
can't use an array object. What can I do to find the cause?
Code example:
class my extends Thread {
public function __construct() {
$this->arr = array();
$this->id = 0;
}
public function run() {
while (true) {
$this->wait();
}
}
public function add() {
$this->id = rand(0, 1000);
$this->arr[] = rand(0, 1000);
var_dump($this->id);//this is rand
var_dump($this->arr);//this is empty array()
$this->notify();
}
}
$my = new my();
$my->start();
while (true) {
sleep(1);
$my->add();
}
PHP is a shared nothing environment: This means that each process (or thread) must have it's own copy of the interpreter, all modules, and user code.
The HashTable
structure which not only supports PHP arrays, but is used throughout the PHP code base, was never intended to be manipulated by multiple contexts.
The memory manager, which would be invoked whenever you set a new member of an array (equivalent of malloc), unset one (equivalent of free), or update one (equivalent of free then malloc), is an integral part of the shared nothing architecture, and among other things is specifically designed to disallow any context to free memory that was allocated by another context, since this constitutes a violation of shared nothing.
The Virtual Machine assumes that it is the only context manipulating an array.
All extension code makes the same assumption.
The consequences for ignoring the rules - share nothing - are dire: You crash PHP.
All of this makes allowing you to store and manipulate an actual array in multiple contexts unrealisable, and should make it undesirable whatever.
Arrays will be serialized upon setting them as a member of a Threaded
object.
You should replace your usage of arrays with Threaded
objects.
A Threaded
object can be manipulated as if it were an array.
Here is something to get you started:
<?php
class Test extends Thread {
public function __construct(Threaded $storage) {
$this->storage = $storage;
}
public function run(){
$i = 0;
while(++$i < 10) {
$this->storage[]=rand(0,1000);
}
$this->synchronized(function($thread){
$thread->stored = true;
$thread->notify();
}, $this);
}
}
$storage = new Threaded();
$my = new Test($storage);
$my->start();
$my->synchronized(function(Thread $thread){
while (!$thread->stored) {
$thread->wait();
}
}, $my);
var_dump($storage);
?>
pthreads v3 (PHP7) introduces the concepts of automatic immutability for Threaded
objects.
Quote from my blog post on immutability in pthreads v3:
In pthreads v3, setting a member of a
Threaded
object (A) to anotherThreaded
object (B) makes the reference that A holds to B immutable.
Immutability is a performance optimization.
Obviously most use cases for arrays involve mutating the array, which Threaded
objects are now not always able to support.
In this particular case, none of the members of the Threaded
array are Threaded
.
pthreads v3 (PHP7) introduces the concept of Volatile
objects.
Volatile, Adjective: Liable to change rapidly and unpredictably, especially for the worse.
Volatile
objects are slower than Threaded
objects because they cannot benefit from the performance optimizations that immutability allow us to make.
Volatile
objects do serve as good alternative to arrays in pthreads v3. pthreads will coerce arrays to Volatile
objects when they are set as members of Threaded
objects:
<?php
class Test extends Thread {
public function run(){
$array = [
"Hello",
"World"
];
var_dump($array);
$this->array = $array;
var_dump($this->array);
}
}
$test = new Test();
$test->start() && $test->join();
?>
Will yield:
array(2) {
[0]=>
string(5) "Hello"
[1]=>
string(5) "World"
}
object(Volatile)#2 (2) {
[0]=>
string(5) "Hello"
[1]=>
string(5) "World"
}
This causes $this->array
to behave as expected during the runtime of the Thread
.
There is a side effect, illustrated by the output of the following code:
<?php
class Test extends Thread {
public function __construct(array $array) {
$this->array = $array;
}
public function run(){
var_dump($this->array);
}
}
$array = [
"Hello",
"World"
];
$test = new Test($array);
$test->start() && $test->join();
var_dump($array);
?>
Will yield:
object(Volatile)#2 (2) {
[0]=>
string(5) "Hello"
[1]=>
string(5) "World"
}
array(2) {
[0]=>
string(5) "Hello"
[1]=>
string(5) "World"
}
Notice that the Volatile
object in the Thread
is disconnected from the array
that was supplied to it's constructor, so that the main context is still manipulating an array
.
Automatic coercion serves to reduce the wtfs-per-minute rate when a Thread
manipulates an array that was passed in from another source.
It's always better to be explicit; Not relying on coercion is the best option.
If you already know that some dependencies will be arrays, then deal with that before setting them as members, avoiding coercion entirely.
It is possible to avoid automatic coercion to Volatile
by using an explicit cast:
<?php
class Test extends Thread {
public function run() {
$this->result = (array) [
"Hello" => "World"
];
}
}
$test = new Test();
$test->start() && $test->join();
var_dump($test->result);
?>
Will yield
array(1) {
["Hello"]=>
string(5) "World"
}
As the example code shows, this is useful for when you really do want to use an array to store a result. As with PHP5, the array will be serialized for storage.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With