在脚本中,我正在尝试检查是否已使用MySQL GET_LOCK
运行相同的脚本。问题是,当脚本试图获得锁定时,它不是空闲的,无论我提供什么参数,它都会永久阻塞。
<?php
class Controller_Refresher extends Controller {
public function action_run($key) {
echo date('H:i:s'."\n", time());
$this->die_if_running();
$this->run();
echo date('H:i:s'."\n", time());
}
private function die_if_running() {
$result = DB::query(Database::SELECT, "SELECT IS_FREE_LOCK('refresher_running') AS free")->execute();
if (! intval($result[0]['free'])) die('Running already');
$result = DB::query(Database::SELECT, "SELECT GET_LOCK('refresher_running', 1)")->execute();
}
private function run() {
echo "Starting\n";
ob_flush();
sleep(10);
DB::query(Database::SELECT, "SELECT RELEASE_LOCK('refresher_running')")->execute();
}
}
当我在浏览器的2个标签中运行它时,我得到了例如:
-tab 1-
20:48:16
Starting
20:48:26
-tab 2-
20:48:27
Starting
20:48:37
虽然我想要做的是制作第二个标签die('Running already');
。
答案 0 :(得分:2)
注意 - 这个问题可能实际上是由php锁定会话文件引起的:
https://stackoverflow.com/a/5487811/539149
因此,您应该在任何需要并发运行的代码之前调用session_write_close()。我在尝试这个Mutex课程后发现了这个:
http://code.google.com/p/mutex-for-php/
该课程运行良好但我的php脚本仍在逐个运行!
此外,您不需要IS_FREE_LOCK()。只需调用GET_LOCK('refresher_running',0),如果它给你锁定,它将返回1,如果锁定,则返回0。这种方式更具原子性。当然,锁定超时在某些情况下仍然有用,例如当您想要排队任务时,但如果同时请求太多,请注意脚本超时。
扎克莫里斯
答案 1 :(得分:0)
一种选择是依赖文件系统锁而不是数据库。由于这是需要处理的脚本执行,因此无关紧要。来自manual的带有非阻塞独占锁的示例:
$fp = fopen('/tmp/lock.txt', 'r+');
/* Activate the LOCK_NB option on an LOCK_EX operation */
if(!flock($fp, LOCK_EX | LOCK_NB)) {
die('Running already');
}
/* ... */
fclose($fp);
修改强>
另一种选择是使用在每次执行开始时创建的状态文件,并在脚本完成后由register_shutdown_function
自动删除。
脚本只会检查状态文件的存在,如果它已经存在,执行将停止:
define('statusFile', sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'myjob.running');
//
// If a process is already running then exit
//
if (file_exists(statusFile)) {
die('Running already');
} else {
file_put_contents(date('Y-m-d H:i:s'), statusFile);
}
//
// Other code here
//
function shutdown() {
unlink(statusFile);
}
//
// Remove the status file on completion
//
register_shutdown_function('shutdown');