通过序列化闭包使脚本失败


Make a script fail by serialization of Closure

使用 PHP 线程,我遇到了几次"闭包序列化"致命错误的问题。但是我不明白它是怎么发生的:我不会将闭包或任何类型的可调用传递给另一个线程,有时当我在项目外部获取代码并单独运行时,它可以工作,但无法进入项目。

所以我试图通过序列化闭包来使脚本失败,以了解它的实际工作原理。

这段代码使用闭包并且实际工作:

<?php
class MyWorker extends Worker {
    protected $kernel;
    protected $container;
    public function __construct($closure)
    {
        echo "Worker::__construct()";
        echo PHP_EOL;
        echo $closure();
        echo PHP_EOL;
    }
    public function run()
    {
        echo "Worker::run()";
        echo PHP_EOL;
    }
}
class Toto {
    protected $c;
    public function __construct()
    {
        $this->c = function() {
            return "CLOSURE";
        };
    }
    public function go()
    {
        $c = $this->c;
        $pool = new Pool(4, MyWorker::class, [function(){
            return "WORKER_CLOSURE";
        }]);
        $pool->submit(new class ($c) extends Threaded {
            public function __construct($c)
            {
                $this->c = $c;
            }
            public function run()
            {
                $c = $this->c;
                echo $c();
                echo "Threaded::run()";
                echo PHP_EOL;
            }
        });
    }
}
$toto = new Toto;
$toto->go();

独自奔跑时。但是在 Symfony 命令中,以下代码根本失败。我不明白为什么因此我无法修复它:

<?php
use Threaded;
use Thread;
class Foo
{    
    public function run()
    {
        $t = new Thread;
        $t->start();
        $t->join();
    }
}

所以我想可能是因为这段代码是在闭包中执行的(我不知道,但也许),所以我尝试了这个:

$c = function() {
    $t = new class extends Thread {
        public function run()
        {
            echo "yo";
        }
    };
    $t->start();
    $t->join();
};
$c();

它有效。因此,将闭包传递给线程是有效的,从闭包内部构建、启动和加入线程也可以,我现在不知道在哪里看......

我也看过这个:在线程中存储闭包失败,但代码已经过时,现在(PHP 7,pthreads 3.x)可以工作。所以在这里我寻求帮助以了解关闭的实际问题是什么......

最新版本的

pthreads允许序列化Closure对象;当用作Threaded对象的成员属性时,pthreads创建Closure关闭以在其他上下文中执行的函数的副本。

Zend仍然不允许序列化Closure对象:

class Member {
    public function __construct(Closure $closure) {
        $this->closure = $closure;
    }
    public $closure;
}
class Fail extends Thread {
    public function __construct(Member $member) {
        $this->member = $member;
    }
    public function run() {
        ($this->member->closure)();
    }
    private $member;
}
$member = new Member(function(){
    echo "can only work if Member is Threaded !'n";
});
$fail = new Fail($member);
$fail->start() && $fail->join();

上面的代码将无法按原样工作,因为Member设置为Threaded对象的成员时被序列化,这会导致pthreads尝试序列化Closure

声明成员:

class Member extends Threaded

将导致它按预期运行。

我对Symfony不够熟悉,无法告诉您在哪里解决问题,但这肯定是它的原因;Closure被设置为对象的成员属性,pthreads随后尝试序列化该对象。