File locking文件锁定是一种架构,通过在任何特定时间点只允许一个用户或进程,来限制访问电脑上的文件。早在1963年这种架构就被引入,但在Symfony中初登场是从2.6版起。

全新的LockHandler类,提供了一个简单的抽象层,借助一个file lock来锁定任何东西。它的最常见使用场景通过锁定命令来避免竞争现象,因此同一命令不能被不同的线程并发执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\LockHandler;
 
class UpdateContentsCommand extends Command
{
    protected function configure()
    {
        // ...
    }
 
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        // create the lock 创建锁定
        $lock = new LockHandler('update:contents');
        if (!$lock->lock()) {
            $output->writeln('The command is already running in another process.');
 
            return 0;
        }
 
        // ... do some task 进行一些操作
 
        // (optional) release the lock (otherwise, PHP will do it
        // for you automatically)
        // (可选的)释放锁定(否则,PHP将替你自动完成)
        $lock->release();
    }
}

LockHandler的构造器把第一个参数当作锁定识别符,将被用于创建锁定的文件的名字的一部分。默认情况是,锁定被创建在系统的临时文件夹中。如果你希望使用一个特定目录,可以将其作为构造器的第二个可选参数传入。

如果锁定被接受,lock()方法返回true,否则返回false。此外,你还可以“可选地”传入一个布尔值参数,用于指明是否需要“在请求的锁定被释放之前进行等待”:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class UpdateContentsCommand extends Command
{
    // ...
 
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        // create the lock 创建锁定
        $lock = new LockHandler('update:contents');
 
        // wait for the lock release as long as necessary 等待锁定释放
        if (!$lock->lock(true)) {
            $output->writeln('The command is already running in another process.');
 
            return 0;
        }
 
        // ...
    }
}

锁定控制器被刻意限制为“基于文件的锁定”,因为要让它对网络或数据库进行锁定将会无比复杂。这意味着它只能工作在“仅使用一个宿主”时。如果你有多个宿主,你就无法使用这个helper。