Цель состоит в том, чтобы иметь скрипт PHP с определенным разделом кода, который может выполняться только одним потоком / процессом за один раз.
Некоторые ограничения:
- Семафоры недоступны в моей системе
- В руководстве упоминается, что на многопоточные серверы нельзя полагаться на flock (), поэтому flock () отсутствует.(подтвердил это)
Итак, я подумал, что было бы возможно (почему нет?) использовать MySQL для синхронизации, но я получаю неожиданные результаты.
Для тестирования у меня есть двавкладки браузера открываются с помощью скрипта test.php
<pre></p>
<p>mysql_connect($dbhost, $dbusername, $dbpassword);
mysql_select_db($database_name);</p>
<p>$sql = "SELECT my_val FROM <code>my_table
WHERE my_var
= 'status' '; $ result = mysql_query ($ sql) или die (mysql_error ()); $ row = mysql_fetch_array ($результат, 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' ГДЕmy_var
= 'status' "; mysql_query ($ sql);
sleep (10); // здесь немного поработать. Echo 'готовая работа
';
//покиньте критическую секцию. пусть остальные знают $ sql = "UPDATE my_table
SET my_val
= 'NOT_RUNNING' WHERE 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.
Некоторые странные вещи:
- Когда я повторяю вышеупомянутый эксперимент, я запускаю обе вкладки в FireFox, а затем в третьемдругой тип браузера, скажем Chrome, тогда он работает!(состояние устанавливается в RUNNING во время сна, сценарий завершается рано, когда состояние RUNNING)
- Когда я повторяю описанный выше эксперимент с использованием двух разных окон командной строки и запускаю сценарий из командной строки, он работает!
- Я проверяю phpMyAdmin во время ожидания, статус обновляется корректно.
Я перепробовал все, блокировка таблицы, транзакции, УСТАНОВИТЬ УРОВЕНЬ ИЗОЛЯЦИИ ГЛОБАЛЬНОЙ ТРАНЗАКЦИИ ЧИТАТЬ НЕОБЕСПЕЧЕННЫЙ, УСТАНОВИТЬ autocommit = 1;, но всегда всегда один и тот же результат.
Кто-нибудь знает, что здесь происходит?Есть ли какое-нибудь хорошее решение для этой проблемы?
Проверенные системы: - Win, MySQL 5.0, php 5.3 - Linux, MySQL 5.0.91-community-log, php 5.2.12
I 'Я застрял здесь, спасибо, что заглянули!
ОБНОВЛЕНИЕ:
Спасибо за отправку ваших ответов - я все еще не могу решить эту проблему.Вот код с 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']."<br>\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<br>';
// 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();