This question follows this answer : Why not all threads are completed?
I've tried to implement Joe Watkins's exemple into a couple of classes :
SynchronizedWork
:
<?php
class SynchronizedWork extends Threaded {
/**
* @var int Dummy work ID
*/
protected $id;
/**
* @var $resultBag Volatile Result container passed around threads
*/
protected $resultBag;
/**
* @var bool Should this work be collected for garbage ?
*/
protected $garbage = 0;
public function __construct($id)
{
$this->id = $id;
}
public function getId()
{
return $this->id;
}
public function setResultBag(Volatile $volatile)
{
$this->resultBag = $volatile;
return $this;
}
/**
* @return $this Set this work ready to be collected for garbage
*/
public function setGarbage()
{
$this->garbage = 1;
return $this;
}
/**
* @return bool Is this work ready to be collected for garbage ?
*/
public function isGarbage() : bool
{
return $this->garbage === 1;
}
public function run()
{
$this->send(range(0, 10, $this->getId()));
}
public function send($result)
{
$this->resultBag->synchronized(function(Volatile $results, $workResult){
$results[$this->getId()] = $workResult;
$results->notify();
}, $this->resultBag, $result);
}
}
SynchronizedPool
:
class SynchronizedPool extends Pool {
/**
* @var int Pool's lifetime submitted works count
*/
protected $submitted = 0;
/**
* @var int Pool's lifetime collected works count
*/
protected $collected = 0;
protected $resultBag;
public function __construct($size, $clazz = Worker::class, $ctor = array())
{
$this->resultBag = new Volatile;
parent::__construct($size, $clazz, $ctor);
}
/**
* @param Threaded $threaded Submit the work to the pool and will be dispatched among Pool's Workers
* @return int|void
*/
public function submit(Threaded $threaded)
{
++$this->submitted;
$threaded->setResultBag($this->resultBag);
parent::submit($threaded);
}
public function getResultBag()
{
return $this->resultBag;
}
/**
* @return int Return the number of work submitted since the Pool has been created
*/
public function submitted()
{
return $this->submitted;
}
/**
* @return array Return the list of workers in the stack. Collected workers are no longer in this stack.
*/
public function getWorks() : array
{
return $this->workers;
}
/**
* @return int Return the number of work collected for garbage since the Pool has been created
*/
public function collected()
{
return $this->collected;
}
public function processResult(Closure $closure)
{
$found = 0;
$resultBag = $this->getResultBag();
do {
$workResult = $this->getResultBag()->synchronized(function() use(&$found, $resultBag) {
while (!count($resultBag)) {
$resultBag->wait();
}
$found++;
return $resultBag->shift();
});
$closure($workResult);
} while ($found < $this->submitted());
}
}
And the execution :
$pool = new SynchronizedPool(3);
$pool->submit(new SynchronizedWork(1));
$pool->submit(new SynchronizedWork(2));
$pool->submit(new SynchronizedWork(3));
$pool->processResult(function($workResult) {
var_dump($workResult);
});
while($pool->collect()) continue;
$pool->shutdown();
It appears to fail with :
PHP Fatal error: Uncaught RuntimeException: pthreads detected an attempt to connect to an object which has already been destroyed in /mnt/hgfs/IdeaProjects/backoffice-data/web/pthreads.php:137
Stack trace:
#0 /mnt/hgfs/IdeaProjects/backoffice-data/web/pthreads.php(137): Threaded->shift()
#1 [internal function]: SynchronizedPool->{closure}()
#2 /mnt/hgfs/IdeaProjects/backoffice-data/web/pthreads.php(138): Threaded->synchronized(Object(Closure))
#3 /mnt/hgfs/IdeaProjects/backoffice-data/web/pthreads.php(154): SynchronizedPool->processResult(Object(Closure))
#4 {main}
thrown in /mnt/hgfs/IdeaProjects/backoffice-data/web/pthreads.php on line 137
I've also tried to call processResult before submit, thinking maybe the ref are destroyed right after the end (even if I didn't call collect), but in this case, it just wait forever.
Why does it say resultBag has been destroyed ? The ref is still hold in Pool isn't it ? What am I doing/understanding wrong ?