Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A PHP/pthreads Thread class can't use array?

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();
   } 
like image 410
user2058508 Avatar asked Feb 10 '13 10:02

user2058508


1 Answers

The Problem

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.

PHP5

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

PHP7

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 another Threaded 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.

like image 173
Joe Watkins Avatar answered Oct 16 '22 20:10

Joe Watkins