目标是拥有一个 PHP 脚本,其中包含特定部分的代码,一次只能由一个线程/进程执行。
一些限制:
- 我的系统上没有信号量
- 手册中提到在多线程服务器中不能依赖flock(),所以flock() 被淘汰了。(证实了这一点)
所以认为有可能(为什么不呢?)使用 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)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' ";
mysql_query ($sql);
表格是:
创建表`my_table`( `my_var` varchar(255) NOT NULL default '', `my_val` varchar(255) NOT NULL default '', 主键(`my_var`) ) 引擎=InnoDB 默认字符集=latin1; 插入“my_table” 价值观( '状态','NOT_RUNNING' )
我转到第一个浏览器选项卡并访问脚本。我等待 5 秒钟,转到另一个选项卡并访问脚本,以便两个脚本并行运行。我希望第一个选项卡等待,第二个选项卡应该在第一个查询后退出。但是,两个选项卡都在 sleep() 行上等待。这意味着第一个查询总是返回 'NOT_RUNNING' 。
一些奇怪的事情:
- 当我重复上述实验时,我在 FireFox 中运行两个选项卡,然后在第三种不同的浏览器类型中运行,比如 Chrome,然后它就可以工作了!(睡眠时状态设置为 RUNNING ,当状态为 RUNNING 时脚本提前退出)
- 当我使用两个不同的命令行窗口重复上述实验并从命令行运行脚本时,它可以工作了!
- 我在等待时检查 phpMyAdmin,状态得到正确更新。
我已经尝试了一切,锁定表、事务、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']."<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();