稍微解释一下,我有一个 Symfony2 设置。我正在使用我扩展的抽象命令类。我希望这些批次中的任何一个只能运行一次。我的目标是制作一个我打开并聚集的锁定文件,以便当 php 脚本以任何可能的方式终止时自动释放锁定。
为了实现这一点,我创建了一个名为 Lock 的类。此类扩展了 SplFileObject 并且基本上是在某处(通常是 /var/lock/*)创建 *.lock 的包装器。现在我在检测这个锁时遇到了问题。我确实有一个使用 fopen 和flock 的设置。由于某种原因,它不会再检测到它了。
我创建了一个 OOP 结构来基本上做我想做的事:
- 确定锁定文件的名称(使用文件夹)
- 创建一个锁对象
- 如果目录不存在,则创建目录并锁定文件
- 调用 SplFileObject::__construct()
- 锁定文件
我不能让它与句柄和 spl 文件对象一起使用。如果我运行脚本,让它休眠 15 秒并在另一个控制台中运行相同的脚本,我将得到脚本成功锁定文件的结果,flock 返回 true。如果我在同一个脚本中的同一个锁文件上创建 2 个 Lock 对象,我在第一个锁上得到 true,在第二个上得到 false,这意味着它第二次无法获得锁。该脚本似乎有效。
但是,当我在两个脚本中使用 2 个锁运行脚本 2 次时,我在两个脚本上都得到 True 和 false ......这意味着它似乎没有跨脚本正确锁定文件:/
有没有人可以告诉我我做错了什么?我检查了文件名,两次运行脚本时它都相同。我尝试了多种权限,例如 777、755、733,但没有区别。
我这样称呼它(只是课程的一部分):
abstract class AbstractTripolisCommand extends ContainerAwareCommand
{
[...]
/**
* Locks the current file based on environments
*
* @param string $application_env
* @param string $symfony_env
*/
private function lockCommand($application_env, $symfony_env)
{
$lock_name = "tripolis/$application_env/$symfony_env/" . crc32(get_class($this));
$lock = new Lock($lock_name, 'w+', $this->getContainer()->get('filesystem'));
var_dump($lock->lock());
$lock2 = new Lock($lock_name, 'w+', $this->getContainer()->get('filesystem'));
var_dump($lock2->lock());
// results when ran 2 times at the same time
// bool(true)
// bool(false)
// when I run this script twice I expect the second run at the same time
// bool(false)
// bool(false)
if(!$lock->lock()) {
throw new Tripolis\Exception('Unable to obtain lock, script is already running');
}
}
[...]
}
锁.php
namespace Something\Component\File;
use Symfony\Component\Filesystem\Filesystem;
/**
* Creates a new SplFileObject with access to lock and release locks
* Upon creation it will create the lock file if not exists
*
* The lock works by keeping a stream open to the lock file instead
* of creating/deleting the lock file. This way the lock is always
* released when the script ends or crashes.
*
* create a file named /var/lock/something/something.lock
* <example>
* $lock = new Lock('something');
* $lock->lock();
* $lock->release();
* </example>
*
* create a file named /var/lock/something/my-lock-file.lock
* <example>
* $lock = new Lock('something/my-lock-file');
* $lock->lock();
* </example>
*
* NOTE: locks that are not released are released
* automatically when the php script ends
*
* @author Iltar van der Berg <ivanderberg@something.nl>
*/
class Lock extends \SplFileObject implements Lockable
{
/**
* @param string $file_name
* @param string $open_mode
* @param Filesystem $filesystem
* @param string $lock_directory
*/
public function __construct($file_name, $open_mode = 'r+', Filesystem $filesystem = null, $lock_directory = '/var/lock')
{
$filesystem = $filesystem ?: new Filesystem();
$file = self::touchLockFile($file_name, $lock_directory, $filesystem);
parent::__construct($file, $open_mode);
}
/**
* Returns true if the lock is placed, false if unable to
*
* @return boolean
*/
public function lock()
{
return $this->flock(LOCK_EX | LOCK_NB);
}
/**
* Returns true if the lock is released
*
* @return bool
*/
public function release()
{
return $this->flock(LOCK_UN);
}
/**
* Attempts to create a lock file for a given filename and directory
* it will return a string if the file is touched
*
* @param string $file_name
* @param string $lock_directory
* @param Filesystem $filesystem
* @return string
*/
private static function touchLockFile($file_name, $lock_directory, Filesystem $filesystem)
{
$lock_file_path = explode('/', $file_name);
$lock_file = array_pop($lock_file_path);
$path = "$lock_directory/" . (empty($lock_file_path)
? $lock_file
: implode('/', $lock_file_path));
$lock_file = "$path/$lock_file.lock";
if(!$filesystem->exists($path) || !is_dir($path)) {
$filesystem->mkdir($path);
}
return $lock_file;
}
}
?>