【发布时间】:2011-09-11 02:20:01
【问题描述】:
所以我有一个调用另一个单例类的主类,但是在运行多个线程(或并发线程)时,我得到了交叉数据污染。这是一个非常简单的版本来解释问题。所有的变量 setter/getter 都在 Singleton 中,由主类调用和设置。
class A {
public function doSomething($var) {
Singleton::instance()->setVar($var);
}
public function showSomething() {
return Singleton::instance()->getVar();
}
}
// 单例
class Singleton {
private static $instance = null;
private $var;
public static function instance() {
if(!isset(self::$instance)) {
$c = __CLASS__;
self::$instance = new $c;
}
return self::$instance;
}
public function setVar($var) {
$this->var = $var;
}
public function getVar() {
return $this->var;
}
}
测试脚本 1:
$actions = Array(
'one',
'two',
'three',
);
foreach($actions as $act) {
$action = new A();
$action->doSomething($act);
echo "Action: ".$action->showSomething()."\n";
sleep(2);
}
测试脚本 1 输出将有;
one
two
three
测试脚本 2:
$actions = Array(
'1',
'2',
'3',
);
foreach($actions as $act) {
$action = new A();
$action->doSomething($act);
echo "Action: ".$action->showSomething()."\n";
sleep(2);
}
测试脚本 2 输出将有;
1
2
3
one
two
three
(不按此顺序,可能缺少其中一个值)
那么当同时执行两个脚本时,为什么测试 1 会包含在测试 2 结果中?
我的测试方式:
打开两个终端,在每个终端中执行一个脚本(因此进入睡眠状态),这样我就可以看到数据污染了。
【问题讨论】:
-
你不能实例化一个新的单例实例,所以所有的对象每次都使用同一个实例。工厂模式可能适合这里
-
小小的挑剔,而不是
$c = __CLASS__; self::$instance = new $c;,你可以只做self::$instance = new self; -
@dqhendricks 您需要了解单例的静态实例,即使 100 个单独的实例同时运行也保持不变
-
W-e-i-r-d。我们都知道这两个脚本应该有自己的用户空间。如果这里没有提供令人满意的答案,我建议您提交boo-boo report on php.net。
-
拥有一个静态变量有什么意义,如果每个线程都为一个静态变量分配不同的内存,那么对于数百万用户到单个数据库的如此多的连接,问题将仍然存在拥有与单个数据库的数百万个打开的连接。静态内存意味着该变量将保留在服务器内存中的位置,以供每个线程使用它。这就是为什么它最常用于数据库连接
标签: php class design-patterns scope