Intereting Posts
загрузка кодаигнита не работает на linux случайный шрифт для текста сайта? mysql_connect: php_network_getaddresses: getaddrinfo не удалось: такой хост не известен, используя значения файлов Почему я получаю такие точные результаты от `filesize`? Как подключиться к PostgreSQL без указания имени базы данных? PHP: Предупреждение: sort () ожидает, что параметр 1 будет массивом, ресурс указан Smarty, как получить первый индекс от foreach? json_encode дает ошибку рекурсии Отправить письмо по электронной почте в Codeignign с помощью Gmail Как переопределить метод CActiveDataProvider, чтобы показать CGridview в порядке убывания по умолчанию в Yii Как настроить коды БД для форм Исключить представление из макета? поведение php flock, когда файл заблокирован одним процессом PHP Специальная функция SMTP-почты Возврат ERROR fputs send bytes failed errno = 32 Разбитая труба Должна ли GD так много памяти при изменении размера?

pthread Объекты темы восстанавливают свое состояние

Недавно работая с расширением pthreads , я обнаружил аномалию. У меня простой объект с внутренним состоянием:

class Sum { private $value = 0; public function add($inc) { $this->value += $inc; } public function getValue() { return $this->value; } } 

Теперь я создал класс Thread, который что-то делает с этим объектом:

 class MyThread extends Thread { private $sum; public function __construct(Sum $sum) { $this->sum = $sum; } public function run(){ for ($i=0; $i < 10; $i++) { $this->sum->add(5); echo $this->sum->getValue() . " "; } } } 

В моей основной функции я создал объект Sum, ввел его в поток и начал его:

 $sum = new Sum(); $thread = new MyThread($sum); $thread->start(); $thread->join(); echo $sum->getValue(); 

Я ожидал, что результат будет равен 50 , потому что поток должен был увеличить значение 10 раз на 5. Но я получил 0 !

Более любопытно, что не синхронизация обратно в основной поток не удалась, но поток даже, кажется, забыл о своем внутреннем состоянии на пути: выход эха внутри метода run() не является ожидаемым 5 10 15 20 25 30 35 40 45 50 но 0 0 0 0 0 0 0 0 0 0 . Никто не мешает нитью – почему он не сохраняет свое состояние?


Замечание: если я не запускаю поток, но вместо этого вызываю метод run () непосредственно в основном потоке ( $thread->run(); ), результат все тот же. Но если я теперь удаляю extends Thread в объявлении класса, он отлично работает и возвращает ожидаемые 5 10 15 20 25 30 35 40 45 50 .

Любой объект, не относящийся к определению pthreads, будет сериализован после установки его членом объекта, происходящего из pthreads.

Операции, такие как + = и [], используют указатели внутри, сериализация несовместима с указателями для других объектов. В руководстве на странице введения указано, что любой объект, который должен управляться несколькими контекстами, должен расширять Stackable, Thread или Worker, например

 <?php class Sum extends Stackable { private $value = 0; public function add($inc) { $this->value += $inc; } public function getValue() { return $this->value; } public function run(){} } class MyThread extends Thread { public $sum; public function __construct(Sum $sum) { $this->sum = $sum; } public function run(){ for ($i=0; $i < 10; $i++) { $this->sum->add(5); echo $this->sum->getValue() . " "; } } } $sum = new Sum(); $thread = new MyThread($sum); $thread->start(); $thread->join(); echo $sum->getValue(); ?> 

Если Sum не использовал указатели, у вас будет возможность получить ссылку из потокового объекта после объединения.

Это простые операции, вам не требуется синхронизация. Единственный раз, когда вы должны синхронизировать, – это когда вы планируете ждать объекта или уведомлять его.

Объекты, связанные с pthreads, гораздо более подходят для этой среды и никогда не сериализуются.

Пожалуйста, прочитайте введение в руководстве и все примеры в методах, которые вы хотите использовать, чтобы точно узнать, что есть, а затем не стесняйтесь спрашивать, почему 🙂

Я знаю, что пользователи PHP не привыкли делать исследования, но мы нажимаем конверт здесь, вы обнаружите, что есть правильные способы сделать что-то неправильным образом, большинство из них задокументированы на примерах, и все, что не Я уверен, что он будет извлечен из меня на SO и, в конечном итоге, найдет путь к документации.

Я не уверен, что пример, который вы дали, это тестирование объектов в частности, но предоставленный вами код не обязательно должен быть двумя объектами и не должен быть двумя объектами. Рассмотрим следующее:

 <?php class MyThread extends Thread { public $sum; public function run(){ for ($i=0; $i < 10; $i++) { $this->add(5); printf("%d ", $this->sum); } } public function add($num) { $this->sum += $num; } public function getValue() { return $this->sum; } } $thread = new MyThread(); $thread->start(); $thread->join(); var_dump($thread->getValue()); ?> 

Это может быть полезно для вас увидеть еще пару функций в действии с объяснением, так что здесь идет, вот пример для вашего:

 <?php class MyThread extends Thread { public $sum; public function __construct() { $this->sum = 0; } public function run(){ for ($i=0; $i < 10; $i++) { $this->add(5); $this->writeOut("[%d]: %d\n", $i, $this->sum); } $this->synchronized(function($thread){ $thread->writeOut("Sending notification to Process from %s #%lu ...\n", __CLASS__, $thread->getThreadId()); $thread->notify(); }, $this); } public function add($num) { $this->sum += $num; } public function getValue() { return $this->sum; } /* when two threads attempt to write standard output the output will be jumbled */ /* this is a good use of protecting a method so that only one context can write stdout and you can make sense of the output */ protected function writeOut($format, $args = null) { $args = func_get_args(); if ($args) { vprintf(array_shift($args), $args); } } } $thread = new MyThread(); $thread->start(); /* so this is synchronization, rather than joining, which requires an actual join of the underlying thread */ /* you can wait for notification that the thread is done what you started it to do */ /* in these simple tests the time difference may not be apparent, but in real complex objects from */ /* contexts populated with more than 1 object having executed many instructions the difference may become very real */ $thread->synchronized(function($thread){ if ($thread->getValue()!=50) { $thread->writeOut("Waiting for thread ...\n"); /* you should only ever wait _for_ something */ $thread->wait(); $thread->writeOut("Process recieved notification from Thread ...\n"); } }, $thread); var_dump($thread->getValue()); ?> 

Это объединяет некоторые из более продвинутых функций в некоторых простых примерах и прокомментировано, чтобы помочь вам. Что касается объектов совместного доступа, нет ничего плохого в передаче объекта Thread, если он содержит некоторые функциональные возможности и данные, необходимые для других потоков или стеков. Вы должны стремиться использовать как можно меньше потоков и объектов, чтобы выполнить работу.

Ваша проблема в том, что вы получаете доступ к переменной из основного потока и из потока MyThread. CPU кэширует переменную, и она обновляется в кеше для MyThread, но не в кеше для основного потока, поэтому ваши обои не видят изменений нитей других. В Java / C и т. Д. Есть ключевое слово volatile, но я не знаю, существует ли это в PHP.

Я думаю, вы должны попытаться вызвать методы в сумме, синхронизированной ( http://php.net/manual/en/thread.synchronized.php )

Например, вместо:

  $this->sum->add(5); 

Вызов:

  $this->synchronized(function($thread){ $thread->sum->add(5); }, $this); 

Я думал, что тоже делаю что-то неправильно. Как упоминалось [], и другие операции с использованием указателей не будут работать. Это обходной путь:

 class MyThread extends Thread { public $table; public function __construct($data) { $this->table= $data; } public function run(){ //Set temporary array with current data $temp = $this->table; for ($i=0; $i < 10; $i++) { //Add new elements to the array $temp[] = $i . ' New Value'; } //Overwrite class variable with temporary array $this->table = $temp; } } 

Это работает, поскольку мы используем операцию [] для временной переменной функции, и мы устанавливаем ее как переменную класса.

Лучший пример, почему это необходимо:

 class MyThread extends Thread { public $table; public function __construct($data) { $this->table= $data; } public function run(){ $this->addElem(); $temp = $this->table; $temp[] = 1; $this->table = $temp; } protected function addElem() { $temp = $this->table; $temp[] = $i . ' New Value'; $this->table = $temp; } } 

Таким образом, мы можем использовать и добавлять новые элементы к переменной более чем в одной функции. То же самое будет работать для + = и других операторов, но для этого вам нужна временная переменная.

Простота расширения для этого:

 protected function overload($name, $val, $type = '[]'){ $temp = $this->$name; switch($type){ case '[]': $temp[] = $val; break; case '+=': $temp += $val; break; case '.=': $temp .= $val; break; } $this->$name = $temp; } 

Эваль мог работать здесь, но я нахожу это намного безопаснее.