Php pthread线程对象重置其状态
最近使用扩展时,我发现了一个异常。我有一个具有内部状态的简单对象:Php pthread线程对象重置其状态,php,multithreading,pthreads,shared-memory,Php,Multithreading,Pthreads,Shared Memory,最近使用扩展时,我发现了一个异常。我有一个具有内部状态的简单对象: class Sum { private $value = 0; public function add($inc) { $this->value += $inc; } public function getValue() { return $this->value; } } 现在,我创建了一个Thread类,该类对该对象执行某些操作: class MyThread extends Thre
class Sum {
private $value = 0;
public function add($inc) { $this->value += $inc; }
public function getValue() { return $this->value; }
}
现在,我创建了一个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() . " ";
}
}
}
我希望结果是50
,因为线程必须将该值增加10倍5。但是我得到了一个0
更奇怪的是,并不是同步返回到主线程失败了,而是线程似乎在执行过程中忘记了它的内部状态:run()
方法中的回声输出不是预期的5101520303540550
,而是0 0 0 0 0 0 0
。没有人干预线程-为什么它不保持其状态
旁注:如果我不启动线程,而是直接在主线程中调用run()-方法(
$thread->run();
),结果仍然是一样的。但是,如果我现在删除类声明中的扩展线程
,它将正常工作并返回预期的5 10 15 20 25 30 35 40 45 50
您的问题是您正在从主线程和MyThread线程访问变量。CPU缓存变量,它在MyThread的缓存中得到更新,而不是在主线程的缓存中得到更新,因此您的两个线程都不会看到其他线程的更改。在Java/C等语言中,有关键字volatile,但我不知道PHP中是否存在
我认为您应该尝试调用sum synchronized()中的方法
例如,而不是:
$this->sum->add(5);
电话:
任何未从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();
?>
如果Sum没有使用指针,那么您可以选择在连接之后从线程对象检索引用
这些都是简单的操作,不需要同步。唯一应该同步的时间是计划等待对象或通知对象的时间
与pthread捆绑在一起的对象非常适合这种环境,并且从不序列化
请务必阅读手册中的介绍以及您希望使用的方法中的所有示例,以了解到底什么是什么,然后随意询问原因:)
我知道PHP的用户不习惯做研究,但我们正在努力,你会发现有正确的方法来做事情,但方法不正确,大多数都在示例中记录,我不确定的任何东西都会从我这里提取出来,并最终找到到文档中的方法
我不确定你所给出的例子是否特别地测试对象,但是你提供的代码不需要是两个对象,也不应该是两个对象,考虑下面的内容:
<?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());
?>
通过解释,您可能会看到更多的功能正在发挥作用,因此,这里有一个与您类似的示例:
<?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());
?>
这在一些简单的示例中结合了一些更高级的功能,并对其进行了注释以帮助您。关于共享对象,如果线程对象包含其他线程或堆栈中所需的一些功能和数据,那么传递线程对象没有什么错。你应该尽可能少地使用线程和对象来完成任务。我认为我也做错了什么。如前所述,[]和其他使用指针的操作将不起作用。这是解决方法:
class MyThread extends Thread {
public $table;
public function __construct($data) {
$this->table= $data;
}
public function run(){
//Set temporary array with current data
$temp = $this->table;
for ($i=0; $i < 10; $i++) {
//Add new elements to the array
$temp[] = $i . ' New Value';
}
//Overwrite class variable with temporary array
$this->table = $temp;
}
}
因此,我们可以在多个函数中使用并向变量添加新元素。
这同样适用于+=和其他运算符,但需要临时变量
此功能易于扩展:
protected function overload($name, $val, $type = '[]'){
$temp = $this->$name;
switch($type){
case '[]':
$temp[] = $val;
break;
case '+=':
$temp += $val;
break;
case '.=':
$temp .= $val;
break;
}
$this->$name = $temp;
}
Eval可以在这里工作,但我发现这更安全。nono您在线程外定义$sum=new sum(),因此$sum->value属于主线程。您可以尝试在线程内创建sum对象,而不是将sum传递给线程。如果这行得通,我是对的,如果不行,你知道;):-)我只是这样做了:插入$this->sum=newsum();作为run()方法的第一行。但是结果还是一样的。如果答案中的解决方案不起作用,我想你只是在pthread中发现了一个bug。看起来你使用的是旧版本的pthreads。在php.net上,您可以看到这个函数在pthreads 0.40之后是可用的。(另外:pthreads中的所有同步功能似乎都是在版本0.40中添加的,所以您真的应该更新PHP/pthreads)是的,一个类中的代码正是我建议用于测试目的的代码。我认为他需要两个类,因为他想从多个线程计算。我已经扩展了答案,以涵盖这种可能性。。。使用多线程时,使用尽可能少是一天的顺序。衡量效率的标准不是你的应用程序使用了多少线程,而是它使用了多少尽可能少的线程来在最短的时间内完成任务。在PHP中第一次这样做甚至有一些好处,所以其他语言中的许多问题在pthreads中根本不存在。你的假设可能适用于其他语言,但不适用于这里,你可以通过你最初的想法有多复杂和实际解决方案有多简单来看出这一点。但它不需要简单,它和用户编写的一样简单或复杂,其他线程API中没有的东西它们都是复杂的,不管怎样。@th3falc0n:没错。我只是在这里玩。我的第一个测试——thread类中的所有测试——都运行良好。所以我尝试了下一步——注入一个对象——但失败了。但现在我知道了
class MyThread extends Thread {
public $table;
public function __construct($data) {
$this->table= $data;
}
public function run(){
//Set temporary array with current data
$temp = $this->table;
for ($i=0; $i < 10; $i++) {
//Add new elements to the array
$temp[] = $i . ' New Value';
}
//Overwrite class variable with temporary array
$this->table = $temp;
}
}
class MyThread extends Thread {
public $table;
public function __construct($data) {
$this->table= $data;
}
public function run(){
$this->addElem();
$temp = $this->table;
$temp[] = 1;
$this->table = $temp;
}
protected function addElem() {
$temp = $this->table;
$temp[] = $i . ' New Value';
$this->table = $temp;
}
}
protected function overload($name, $val, $type = '[]'){
$temp = $this->$name;
switch($type){
case '[]':
$temp[] = $val;
break;
case '+=':
$temp += $val;
break;
case '.=':
$temp .= $val;
break;
}
$this->$name = $temp;
}