pthread线程对象重置其状态

Fra*_*ois 8 php multithreading pthreads shared-memory

最近使用扩展pthreads工作,我发现了一个异常现象.我有一个内部状态的简单对象:

class Sum {
    private $value = 0;
    public function add($inc)  { $this->value += $inc; }
    public function getValue() { return $this->value; }
}
Run Code Online (Sandbox Code Playgroud)

现在我创建了一个Thread类,它对这个对象做了一些事情:

class MyThread extends Thread {
    private $sum;

    public function __construct(Sum $sum) {
        $this->sum = $sum;
    }

    public function run(){
        for ($i=0; $i < 10; $i++) {
            $this->sum->add(5);
            echo $this->sum->getValue() . " ";
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在我的main函数中,我创建了一个Sum对象,将其注入到线程中并启动它:

$sum = new Sum();
$thread = new MyThread($sum);
$thread->start();
$thread->join();
echo $sum->getValue();
Run Code Online (Sandbox Code Playgroud)

我期望结果是50,因为线程必须将值增加10倍.但是我得到了0!

更奇怪的是,它不是同步回失败,但线程甚至似乎忘了在途中其内部状态的主线程:内部回声的输出run()方法不预期5 10 15 20 25 30 35 40 45 50,但0 0 0 0 0 0 0 0 0 0.没有人干涉线程 - 为什么它不保留其状态?


旁注:如果我没有启动线程而是直接在主线程($thread->run();)中调用run() - 方法,结果仍然是相同的.但是,如果我现在删除extends Thread类声明中的,它完美地工作并返回预期的5 10 15 20 25 30 35 40 45 50.

Joe*_*ins 17

任何不是来自pthreads定义的对象在将其设置为pthreads后代对象的成员时将被序列化.

像+ =和[]这样的操作在内部使用指针,序列化与其他对象的指针不兼容.在介绍页面的手册中,它声明任何打算由多个上下文操作的对象应该扩展Stackable,Thread或Worker,就像

<?php
class Sum extends Stackable {
    private $value = 0;
    public function add($inc)  { $this->value += $inc; }
    public function getValue() { return $this->value; }
    public function run(){}
}

class MyThread extends Thread {
    public $sum;

    public function __construct(Sum $sum) {
        $this->sum = $sum;
    }

    public function run(){
        for ($i=0; $i < 10; $i++) {
            $this->sum->add(5);
            echo $this->sum->getValue() . " ";
        }
    }
}

$sum = new Sum();
$thread = new MyThread($sum);
$thread->start();
$thread->join();
echo $sum->getValue();
?>
Run Code Online (Sandbox Code Playgroud)

如果Sum没有使用指针,您可以选择在连接后从线程对象中检索引用.

这些操作很简单,您不需要同步.您应该同步的唯一时间是您计划等待对象或通知对象.

与pthreads捆绑在一起的对象非常适合这种环境,从不进行序列化.

请阅读手册中的介绍和您希望使用的方法中的所有示例,以确切了解什么是什么,然后随意问为什么:)

我知道PHP的用户不习惯进行研究,但是我们正在推动这方面的工作,你会发现有正确的方法以不正确的方式做事,其中大多数是在示例中记录的,而且任何不是我肯定会从SO中提取出来并最终找到它的文档.

我不确定你给出的例子是否特别测试了对象,但你提供的代码不必是两个对象,也不应该是两个对象,请考虑以下内容:

<?php
class MyThread extends Thread {
    public $sum;

    public function run(){
        for ($i=0; $i < 10; $i++) {
            $this->add(5);

            printf("%d ", $this->sum);
        }
    }

    public function add($num) { $this->sum += $num; }
    public function getValue() { return $this->sum; }
}

$thread = new MyThread();
$thread->start();
$thread->join();
var_dump($thread->getValue());
?>
Run Code Online (Sandbox Code Playgroud)

您可以通过解释看到更多功能,这里有一个与您类似的示例:

<?php
class MyThread extends Thread {
    public $sum;

    public function __construct() {
        $this->sum = 0;
    }

    public function run(){
        for ($i=0; $i < 10; $i++) {
            $this->add(5);

            $this->writeOut("[%d]: %d\n", $i, $this->sum);
        }

        $this->synchronized(function($thread){
            $thread->writeOut("Sending notification to Process from %s #%lu ...\n", __CLASS__, $thread->getThreadId());
            $thread->notify();
        }, $this);
    }

    public function add($num) { $this->sum += $num; }
    public function getValue() { return $this->sum; }

    /* when two threads attempt to write standard output the output will be jumbled */
    /* this is a good use of protecting a method so that 
        only one context can write stdout and you can make sense of the output */
    protected function writeOut($format, $args = null) {
        $args = func_get_args();
        if ($args) {
            vprintf(array_shift($args), $args);
        }
    }
}

$thread = new MyThread();
$thread->start();

/* so this is synchronization, rather than joining, which requires an actual join of the underlying thread */
/* you can wait for notification that the thread is done what you started it to do */
/* in these simple tests the time difference may not be apparent, but in real complex objects from */
/* contexts populated with more than 1 object having executed many instructions the difference may become very real */
$thread->synchronized(function($thread){
    if ($thread->getValue()!=50) {
        $thread->writeOut("Waiting for thread ...\n");
        /* you should only ever wait _for_ something */
        $thread->wait();
        $thread->writeOut("Process recieved notification from Thread ...\n");
    }
}, $thread);

var_dump($thread->getValue());
?>
Run Code Online (Sandbox Code Playgroud)

这结合了一些简单示例中的一些更高级的功能,并进行了评论以帮助您.关于共享对象的主题,如果Thread对象包含其他线程或stackables中所需的某些功能和数据,则传递Thread对象没有任何问题.您应该尽可能少地使用线程和对象来完成工作.