执行锁文件(用于临界区目的)清理的正确方法是什么?


What is the correct way to perform lock file (for having critical section purpose) cleanup

参考flock():在没有竞争条件的情况下移除锁定文件?当进程意外死亡时,flock'ed文件会被解锁吗?

生成以下代码。我的意图是只允许单个线程/单个进程在任何给定的时间内运行临界区代码。

<?php
// Exclusive locking based on function parameters.
$lockFileName = '/tmp/cheok.lock';
// Create if doesn't exist.
$lockFile = fopen($lockFileName, "w+");
if (!flock($lockFile, LOCK_EX)) {
    throw new 'RumtimeException("Fail to perform flock on $lockFileName");
}
echo "start critical section...'n";
sleep(10);
echo "end critical section.'n";
// Warning: unlink(/tmp/cheok.lock): No such file or directory in
unlink($lockFileName);
flock($lockFile, LOCK_UN);

总是会得到警告

警告:unlink(/tmp/check .lock):

中没有这样的文件或目录

当第二个等待进程继续执行时,第一个进程已经删除了物理磁盘文件。第二个进程尝试unlink文件,这个文件已经被第一个进程删除了。

并且,如果有第三个进程加入,并且尝试执行fopen而第二个进程正在尝试执行unlink怎么办?

总之,执行锁文件清理的正确方法是什么?

这个问题被标记为多线程,所以我将使用pthreads在多线程上下文中回答这个问题。

首先,简单介绍一下文件锁定。PHP手册没有完全解释建议锁是什么。

取自flock的手册页

flock()只放置建议锁;如果给定对文件的适当权限,则进程可以自由地忽略flock()的使用,并对该文件执行I/O。

在一个生态系统中,很多人同时不太了解权限,并且使用cron作业来执行长时间运行的脚本,这可能会导致很多痛苦…虽然没有人真正知道。

在多线程的上下文中(使用pthreads),您希望远离文件锁定,pthreads提供了一个更好的API来实现互斥。

下面是创建多个线程并实现互斥的示例代码:

<?php
class Test extends Thread {
    public function __construct(Threaded $monitor) {
        $this->monitor = $monitor;
    }
    public function run () {
        $this->monitor->synchronized(function(){
            for ($i = 0; $i < 1000; $i++)
                printf("%s #%lu: %d'n",
                    __CLASS__, $this->getThreadId(), $i);
        });
    }
    private $monitor;
}
$threads = [];
$monitor = new Threaded();
for ($i = 0; $i < 8; $i++) {
    $threads[$i] = new Test($monitor);
    $threads[$i]->start();
}
foreach ($threads as $thread) 
    $thread->join();

因为所有线程共享相同的$monitor,您可以使用内置在Threaded对象中的同步来实现可靠的互斥,从而导致输出类似于:

Test #140561163798272: 0
<-- snip for brevity -->
Test #140561163798272: 999
Test #140561151424256: 0
<-- snip for brevity -->
Test #140561151424256: 999
Test #140561138841344: 0
<-- snip for brevity -->
Test #140561138841344: 999
Test #140561059149568: 0
<-- snip for brevity -->
Test #140561059149568: 999
Test #140561050756864: 0
<-- snip for brevity -->
Test #140561050756864: 999
Test #140561042364160: 0
<-- snip for brevity -->
Test #140561042364160: 999
Test #140561033971456: 0
<-- snip for brevity -->
Test #140561033971456: 999
Test #140561025578752: 0
<-- snip for brevity -->
Test #140561025578752: 999

我们可以看到,每个Thread都相互排斥执行传递给Threaded::synchronizedClosure中的代码。

您可以通过使用出色的Process Explorer任务管理器来解锁文件。我们之前已经详细介绍了Process Explorer,所以这里我们将直接研究如何解锁文件。您不需要首先安装它——它是一个便携式应用程序——但您需要以管理权限运行它。实际上,您可以在进程资源管理器中单击"文件"菜单并选择"显示所有进程的详细信息"。