Цель состоит в том, чтобы иметь скрипт PHP с определенным разделом кода, который может выполняться только одним потоком / процессом за один раз.
Некоторые ограничения:
Так думал, что было бы возможно (почему нет?) использовать MySQL для синхронизации, но я получаю неожиданные результаты.
Для тестирования у меня есть две вкладки браузера, открытые с помощью скрипта test.php
mysql_connect ($ dbhost, $ dbusername, $ dbpassword); mysql_select_db ($ database_name);
$ sql = "SELECT my_val FROM my_table
WHERE my_var
= 'status'";
$ result = mysql_query ($ sql) или die (mysql_error ());
$ row = mysql_fetch_array ($ result, MYSQL_ASSOC);
echo "status is:". $ row ['my_val']. "
\ n";
mysql_free_result ($ result);
if ($ row ['my_val'] == 'RUNNING') die ('процесс уже запущен!');
// вход в критическую секцию
$ sql = " ОБНОВЛЕНИЕ my_table
SET my_val
= 'RUNNING' WHERE my_var
= 'status' ";
mysql_query ($ sql);
sleep (10); // сделать здесь работу
echo 'готовая работа
';
// покинуть критическую секцию. пусть другие знают
$ sql = "ОБНОВЛЕНИЕ my_table
SET my_val
= 'NOT_RUNNING' ГДЕ my_var
= 'status'";
mysql_query ($ sql);
Таблица:
CREATE TABLE `my_table` ( `my_var` varchar(255) NOT NULL default '', `my_val` varchar(255) NOT NULL default '', PRIMARY KEY (`my_var`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; INSERT INTO `my_table` VALUES ( 'status', 'NOT_RUNNING' )
Я перехожу на первую вкладку браузера и получаю доступ к сценарию. Я жду 5 секунд, перехожу на другую вкладку и получаю доступ к сценарию, чтобы оба сценария выполнялись параллельно. Я ожидаю, что первая вкладка будет ждать, а вторая вкладка должна выйти после 1-го запроса. Однако обе вкладки ожидают в строке sleep (). Это означает, что первый запрос всегда возвращает NOT_RUNNING.
Некоторые странные вещи:
Я перепробовал все, заблокировав таблицу, транзакции, SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED, SET autocommit = 1; но всегда один и тот же результат.
Кто-нибудь знает, что здесь происходит? Есть ли хорошее решение для этой проблемы?
Системы проверены: - Win, MySQL 5.0, php 5.3 - Linux, MySQL 5.0.91-community-log, php 5.2.12
Я застрял здесь, спасибо, что заглянули!
ОБНОВЛЕНИЕ:
Спасибо за отправку ответов - я все еще не могу решить Эта проблема. Вот код с GET_LOCK () и session_write_close (), как было предложено: Я также пробовал блокировку на уровне строк, транзакции и разные уровни изоляции. Возможно, это невозможно сделать?
session_write_close(); mysql_connect($dbhost, $dbusername, $dbpassword); mysql_select_db($database_name); $sql = "CREATE TABLE IF NOT EXISTS `my_table` ( `my_var` varchar(255) NOT NULL default '', `my_val` varchar(255) NOT NULL default '', PRIMARY KEY (`my_var`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; "; mysql_query($sql)or die(mysql_error()); mysql_query ("SELECT get_lock('my_lock', 100)"); $sql = "SELECT my_val FROM `my_table` WHERE `my_var`='status' "; $result = mysql_unbuffered_query($sql)or die(mysql_error()); $row = mysql_fetch_array($result, MYSQL_ASSOC); echo "status is:".$row['my_val']."
\n"; mysql_free_result($result); if ($row['my_val']=='RUNNING') die ('process is already running!'); // entering critical section $sql = "UPDATE `my_table` SET `my_val`='RUNNING' WHERE `my_var`='status' "; mysql_query ($sql); sleep(10); // do some work here. echo 'finished work
'; // leave critical section. let the others know //$sql = "UPDATE `my_table` SET `my_val`='NOT_RUNNING' WHERE `my_var`='status' "; $sql = "REPLACE INTO `my_table` (`my_var`,`my_val`) VALUES ('status', 'NOT_RUNNING')"; mysql_query ($sql); $result = mysql_query($sql)or die(mysql_error()); mysql_query ("SELECT release_lock('my_lock')"); die();
Второе окно браузера не зависает при выполнении команды sleep
. Он блокируется, потому что сеансы блокируются (и, следовательно, он пытается открыть тот же сеанс). Вы можете закрыть текущий сеанс, если он вам больше не нужен (и, следовательно, не хотите, чтобы он блокировался) с помощью session_write_close
...