Синхронизированный блок в php 7

я родом из фона Java, где были синхронизированные блоки:

Ключевые слова «Синхронизированные» предотвращают одновременный доступ к блоку кода или объекту несколькими потоками.

пример кода в java:

public void addName(String name) {
    synchronized(this) {
        lastName = name;
        nameCount++;
    }
    nameList.add(name);
}

Теперь этот пример подчеркивает фундаментальную разницу в php и java (поправьте меня, если я ошибаюсь). Но синглтон или общий класс не существует в php. Таким образом, данный пример кода Java имеет большой смысл при использовании в качестве синглтона. Таким образом, имея общий объект между запросами. Теперь этого, к сожалению, не существует для php, что, очевидно, является серьезным недостатком. Но чтобы сделать это способом php, это в конечном итоге будет записывать количество имен в файл или базу данных, таким образом, разделяя данные между запросами таким образом (очевидно, намного медленнее). Но проблема останется той же: если 2 запроса одновременно увеличат количество имен, этого будет слишком мало.

Теперь первый вопрос: существует ли что-то подобное для php 7? то есть синхронизированный блок

Теперь я не уверен, действительно ли в php 7 слово thread относится к тому, о чем я беспокоюсь. Считается ли поток в php также отдельным вызовом php-файла, скажем, foo.php, то есть, если я дважды обращаюсь к foo.php одновременно, будет ли синхронизированный блок, если он существует, выполняться только один за другим или делать Мне нужно создать правильный поток php, расширив класс Thread, и только тогда он будет считаться потоком?


person Toskan    schedule 06.08.2016    source источник
comment
Мне кажется проблема XY. Для базы данных вы не можете просто сделать count = count + 1 в запросе? Для файла вы можете использовать php.net/manual/en/function.flock.php   -  person rjdown    schedule 06.08.2016
comment
Возможный дубликат синхронизированных функций с использованием PHP   -  person revo    schedule 06.08.2016
comment
PHP может извлечь выгоду из многопоточности, используя pthreads, API, который позволяет использовать многопоточность. -поточность в PHP. Таким образом, вы также можете выполнить блокировку синхронизации.   -  person revo    schedule 06.08.2016


Ответы (4)


Короткий ответ на ваш вопрос: нет, ничего подобного для PHP не существует, потому что, как вы указываете, PHP не запускает многопоточные процессы. PHP 7 в этом отношении такой же, как и предыдущие версии PHP.

Теперь вы описываете отсутствие многопоточности как главный недостаток. Это не обязательно так: это большая разница. Минус это или нет - другой вопрос. Это очень сильно зависит от контекста.

Проблема, которую вы описываете, заключается в наличии общего объекта между процессами. Общий объект не является последовательным для PHP без многопоточности, но основная цель общего объекта — совместное использование данных внутри объекта.

Если мы говорим об общих данных, вы правы в том, что БД или файл являются распространенным способом сделать это, и обычно этого достаточно с точки зрения производительности, но если вам действительно нужно больше производительности, вы можете действительно поделиться данными в памяти. с помощью чего-то вроде Memcache. Существуют хорошо зарекомендовавшие себя библиотеки для работы с memcache в PHP. Это было бы нормальным решением PHP.

Теперь есть еще два вопроса, которые я хотел бы здесь затронуть и которые могут быть уместны здесь.

Во-первых, позвольте мне добавить NodeJS к уравнению, потому что это снова делает вещи по-другому. В NodeJS система также является однопоточной, но в отличие от PHP, который запускает новый процесс для каждого запроса, в NodeJS все запросы передаются в один постоянно работающий поток. Это означает, что в NodeJS, даже если он однопоточный, вы можете иметь глобальные данные (обычно соединение с БД), которые совместно используются запросами, потому что все они выполняются в одном процессе.

Дело в том, что однопоточность не является причиной того, что PHP не может обмениваться данными между запросами; это больше о том, что в PHP каждый запрос изолирован от других в своем собственном процессе. Это далеко не недостаток, это на самом деле может быть преимуществом — например, сбой PHP не приведет к падению всего вашего сайта, что могло бы произойти в многопоточной среде или среде с разделяемыми потоками. На самом деле это одна из самых больших слабостей NodeJS: один бит плохо написанного кода может сделать сервер полностью невосприимчивым.

Второе, о чем я хотел сказать, это то, что на самом деле существуют экспериментальные ветки PHP, которые действительно позволяют вам использовать язык как для многопоточной среды, так и для среды с разделяемыми потоками. В обоих случаях они являются очень очень экспериментальными и, конечно же, не должны использоваться в производственной среде. Как вы заметили в вопросе, в языке отсутствуют ключевые функции, которые были бы необходимы для использования в этих средах. Но факт в том, что это можно сделать.

Ни один из этих экспериментов ни к чему не приведет, потому что существующий PHP-код написан без учета таких сред. Вы уже знаете, что произойдет, если вы напишете многопоточную Java-программу, не защищая ваши общие данные, поэтому должно быть ясно, что если PHP когда-либо будет серьезно интересоваться работой на платформах с общими данными, то любой, кто захочет использовать его с существующим PHP код должен был бы сделать обширную переписку. Это не просто произойдет, поэтому можно с уверенностью сказать, что PHP будет придерживаться своего текущего формата с изолированными процессами.

person Spudley    schedule 06.08.2016
comment
Что вы подразумеваете под глобальными данными в Node.js? - person revo; 06.08.2016

Другие люди отвечают большим количеством слов, но без кода. Почему бы тебе не попробовать это?

function synchronized($handler)
{
    $name = md5(json_encode(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS,1)[0]));
    $filename = sys_get_temp_dir().'/'.$name.'.lock';
    $file = fopen($filename, 'w');
    if ($file === false) {
        return false;
    }
    $lock = flock($file, LOCK_EX);
    if (!$lock) {
        fclose($file);
        return false;
    }
    $result = $handler();
    flock($file, LOCK_UN);
    fclose($file);
    return $result;
}

function file_put_contents_atomic($filename, $string)
{
    return synchronized(function() use ($filename,$string) {
        $tempfile = $filename . '.temp';
        $result = file_put_contents($tempfile, $string);
        $result = $result && rename($tempfile, $filename);
        return $result;
    });
}

Приведенный выше код выполняет атомарное file_put_contents с использованием пользовательской «синхронизированной» функции.

Источник: https://tqdev.com/2018-java-synchronized-block-in-php

person mevdschee    schedule 09.09.2018

PHP разработан, чтобы быть недолговечным — приложение PHP рождается и уничтожается с помощью HTTP-запроса.

Каждый раз, когда ваш веб-сервер получает HTTP-запрос, он вызывает новый экземпляр вашего приложения. Этот экземпляр живет в своем собственном процессе со своим собственным состоянием.

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

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

person Niels B.    schedule 06.08.2016
comment
это очень неудовлетворительно. Я привожу вам такой пример: PayPal иногда отправляет подтверждение после каждого платежа ДВАЖДЫ в одно и то же время. Теперь я делаю в основном if($confirmed && doesNotExistYet()){ $c = new Customer(); $c->save();} угадайте, что, когда это выполняется без блока синхронизации, все это запишет двух новых клиентов вместо одного. извините, но это плохо - person Toskan; 06.08.2016
comment
@Toskan Да, но оператор synchronized ничего не облегчит. Вы должны создать свое приложение по-разному, например. с помощью ограничений UNIQUE в вашей базе данных. - person Polygnome; 06.08.2016
comment
@Polygnome на самом деле это определение варианта использования ключевого слова synchronized. Вы получаете блокировку фрагмента кода, и только один поток/процесс может получить доступ к этому коду одновременно. Так что synchronized(foo){if($confirmed && doesNotExistYet())...} полностью решит эту проблему. Поправьте меня, если я ошибаюсь - person Toskan; 06.08.2016
comment
Нет, не будет. Потому что это два разных процесса. Javas synchronized не пересекает границы процесса. - person Polygnome; 06.08.2016
comment
@Toskan Вы можете получить блокировку ВНУТРИ процесса на одном сервере с помощью ключевого слова synchronized, но если вы увеличите масштаб своего приложения для работы на нескольких серверах — возможно, как часть избыточной настройки — ваше решение потерпит неудачу, потому что ваша блокировка не быть глобальным. - person Niels B.; 06.08.2016
comment
@Toskan Я не знаю об API PayPal, но если бы он включал идентификатор транзакции, вы могли бы использовать его вместе с ограничением уникальности. Это приведет к сбою второго запроса PayPal. - person Niels B.; 06.08.2016
comment
@НильсБ. хороший момент, я не думал о масштабировании, правда. - person Toskan; 06.08.2016
comment
@Toskan - я столкнулся с аналогичной проблемой, когда адаптивные кассы PayPal несколько раз перенаправляли клиентов. В итоге я использовал функцию flock для синхронизации нескольких вызовов скриптов. Меня тошнит от того, что PHP не имеет надлежащих возможностей синхронизации (для этого не требуется специально скомпилированный двоичный файл). - person Luke A. Leber; 22.01.2017

Одиночки и общие классы существуют в PHP.

Но PHP недолговечен. Для каждого HTTP-запроса создается новый процесс и выполняется ваш скрипт. Это означает, что ключевое слово synchronized в вашем случае не поможет.

Условия гонки между несколькими запросами избегаются дизайном приложения.

Например, в вашем SQL-запросе вы должны делать такие вещи, как count = count + 1, вместо того, чтобы сначала читать count, затем увеличивать его на единицу, а затем записывать. Это не так уж отличается от некоторых механизмов, которые вы использовали бы в Java для параллелизма (synchronized часто является наихудшим средством от проблем с параллелизмом).

Для баз данных вы можете заблокировать таблицы и использовать транзакции для обеспечения целостности нескольких запросов.

Я привожу вам такой пример: PayPal иногда отправляет подтверждение после каждого платежа ДВАЖДЫ в одно и то же время. Теперь я делаю в основном if($confirmed && DoesNotExistYet()){ $c = new Customer(); $с->сохранить();}

Используйте ограничения UNIQUE в своей базе данных. Или закрой стол. Или иначе. У вас есть варианты, но ключевое слово synchronized не решит проблему, потому что это другие процессы.

Несколько слов о синглтонах: Java гарантирует, что один синглтон существует только один раз для каждого процесса. PHP делает то же самое. Если вы запустите один и тот же JAR 2x, вы получите два процесса Java, каждый со своим собственным экземпляром синглтона.

person Polygnome    schedule 06.08.2016
comment
теперь я понимаю, что вы имели в виду под синхронизацией, это не решит, да, это не решит в контексте php. Я думал, вы обращаетесь к java. Просто кажется немного неоптимальным для обеспечения параллелизма с использованием БД. Но я думаю, что для php просто нет плана б. Мне было интересно, изменили ли они что-то с php 7, но, похоже, это не так. - person Toskan; 06.08.2016
comment
@Toskan Многое действительно изменилось в PHP 7, но ничего столь же фундаментального, как модель потоковой передачи - я сомневаюсь, что это можно было бы изменить таким образом, чтобы обеспечить достаточную обратную совместимость, чтобы сделать это стоящим. - person Spudley; 06.08.2016
comment
@Polygnome, как я могу обеспечить безопасность параллелизма, скажем, для редактирования клиента? как бы вы сделали это для заказов? например что-то, что не может полагаться на уникальное ограничение. - person Toskan; 06.08.2016
comment
@Toskan Вы можете сделать это разными способами, и все они слишком широки, чтобы их можно было затронуть в комментарии здесь. Люди веками писали надежные PHP-приложения, доступно много материала. RDBM предлагают отличные инструменты для поддержки согласованности. В этом случае я бы просто сделал один запрос на обновление, которое РСУБД гарантирует атомарность. - person Polygnome; 06.08.2016
comment
хм, я немного подумал, для Order случая, когда существует много заказов на одного клиента. Вероятно, хорошей идеей во избежание дублирования является использование уникальных идентификаторов заказов для каждого заказа, которые мы создаем на ранней стадии, чтобы избежать дублирования и перекрестных ссылок, когда PayPal вызывает обработчик ipn. - person Toskan; 06.08.2016