Сделать сценарий сбой путем сериализации 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). Поэтому здесь я прошу помощи, чтобы понять, что на самом деле проблема с закрытием...

1 ответ

Последние версии 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 впоследствии пытается сериализовать.

Другие вопросы по тегам