【问题标题】:Class variable scope problem, cross data contamination类变量作用域问题,交叉数据污染
【发布时间】: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


【解决方案1】:

您在问题中写道 test1 和 test2 是两个独立的进程。从您提供的示例代码中,我看不到两个脚本都以允许跨进程交换的方式交换数据(不是代码)(例如,通过文件、会话等)。

但是从症状来看,当你执行 test2 时,test1 也会被执行。

为了更好地理解正在发生的事情,您可以将代码添加到您的单例类中,使其与当前所做的相呼应。例如。设置变量或显示它。然后你可以追踪你的问题发生的原因。您应该很快找出问题的根源。

或者,如果您手头有一个支持调试器的 IDE,您也可以通过逐步执行来做到这一点。

class Singleton {
    private static $instance = null;
    private $var;

    public static function instance() {
        if(!isset(self::$instance)) {
            $c = __CLASS__;
            self::$instance = new $c;
        }
        echo 'Singleton Instantiated.',"\n";
        return self::$instance;
    }

    public function setVar($var) {
        echo 'Singleton::setVar(', $var, ').',"\n";
        $this->var = $var;
    }

    public function getVar() {
        echo 'Singleton::getVar(): "', $var, '".',"\n";
        return $this->var;
    }
}

【讨论】:

  • 没有。在他的示例中没有跨数据交换,如果您尝试一下,您会发现它们在各自的内存空间中都按预期运行。
  • 对此没有异议。我很确定 OP 在某个地方犯了错误,答案只是对如何解决它有一点帮助。我认为 OP 有一些其他代码正在运行,并且只给出了他的代码的抽象,所以忽略了实际创建他报告的症状的部分。但这只是一个假设,除非 OP 没有提供更多细节并使用调试输出运行他的脚本。
【解决方案2】:

我有点怀疑,所以我运行了上面的代码,它运行良好

即使在 script1 和 script2 的每个实例中都有数百个值,但在 script1 和 script2 连续循环运行时,它们从来没有一次覆盖彼此的单例属性。

反复尝试了 10 分钟,只是为了完全确定。

OP 要么做错了他的测试,要么缺少了一块拼图。

尽管如此,进程之间不共享单例。当然,正如预期的那样。

【讨论】:

  • 认真的吗?我现在可以关闭这篇文章了吗?
  • OP 的配置中是否存在可能导致此问题的内容?也许是一个过时的 PHP 引擎?
  • 不。我只是想知道你是如何得到这个结果的,如果你确实做到了。
  • 不,我真的不想相信你在拖钓。我很想知道您是如何获得这些结果的。
  • 看看它是示例代码,它不是你所说的巨魔帖子。我已经围绕这个设计模式设计了几个类,它们都可以自己运行,但是如果我从标准类中调用它们,那就是错过了。当你说这可能是我的代码时,你是对的,我想通过一个简单的例子来说明我在做什么
猜你喜欢
  • 2019-12-22
  • 1970-01-01
  • 1970-01-01
  • 2012-11-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多