2

我想为少数 cron 作业实现一个简单的锁定机制,并决定以不同寻常的方式来实现。当涉及到套接字创建/绑定/侦听时,PHP 似乎有点错误。

有效的代码:

<?php
echo 'EXECUTING: '. __METHOD__ . \PHP_EOL;

if(false === ($socket = \socket_create(\AF_INET, \SOCK_STREAM, \SOL_TCP))) {
    die('create');
}

if(false === ($bind = \socket_bind($socket, '127.0.0.1', 4444))) {
    die('bind');
}

if(false === ($listen = \socket_listen($socket))) {
    die('listen');
}

var_dump($socket, $bind, $listen);

这将创建一个套接字,将其绑定到接口等待传入连接。您可以通过调用来检查它:

netstat -anp | grep LIST | grep tcp

如果您采用相同的 PHP 代码并将其放入一个简单的类中,它将不会绑定/侦听。这是我正在谈论的代码:

<?php

class Test
{
    public function lock()
    {
        echo 'EXECUTING: '. __METHOD__ . \PHP_EOL;

        if(false === ($socket = \socket_create(\AF_INET, \SOCK_STREAM, \SOL_TCP))) {
            die('create');
        }

        if(false === ($bind = \socket_bind($socket, '127.0.0.1', 4444))) {
            die('bind');
        }

        if(false === ($listen = \socket_listen($socket))) {
            die('listen');
        }

        var_dump($socket, $bind, $listen);
    }
}

$t = new Test();
$t->lock();

echo 'Working...'. \PHP_EOL;
sleep(60);
echo 'Done.';

执行这段代码,你会看到 var_dump 会这样说:

  1. $socket是一种资源(这是我们想要的)
  2. $bind = true
  3. $listen = true

但代码实际上没有绑定/监听。

我做错了什么?

编辑:

测试:

  • PHP 5.4.4 (Linux)
  • PHP 5.3.3 (Linux)
4

1 回答 1

3

答案很简单:你的$socket变量是一个局部变量,它没有引用,而是在方法范围内。这意味着,一旦方法被执行,PHP 将销毁所有的局部变量——同样如此$socket。IE。您正在成功创建套接字,然后 PHP 在同一时刻取消设置它(因为方法结束了它的执行)。请注意,取消设置局部变量并不意味着立即释放内存。垃圾收集周期很昂贵,所以 PHP 只会在达到内存限制时才会这样做。

要查看差异,sleep()请在创建套接字后在方法内部调用。PHP 从 5.3 版本开始就有垃圾收集,所以你尝试过的版本会受到影响。要实时查看它,您可以查看以下代码段

class Foo
{

    public function __destruct()
    {
        echo('destroyed'.PHP_EOL);
    }
}

class Bar
{
    public function test()
    {
        echo('starting method'.PHP_EOL);
        $obj = new Foo();
        echo('ending method'.PHP_EOL);
    }
}
echo('start script'.PHP_EOL);
$obj = new Bar;
$obj->test();
echo('end script'.PHP_EOL);

结果

启动脚本
启动方式
结束方式
毁坏
结束脚本

- 如您所见,在退出方法调用后立即调用了类析构函数,因为局部$obj变量在其他任何地方都没有引用。

于 2013-11-02T16:22:04.020 回答