这是因为flock()
可能失败不仅是因为在其他地方已经获得了锁。
在这种情况下,它不会阻塞等待锁被释放,但会立即返回 false。换句话说,与LOCK_NB
如果flock返回false并且wouldblock=1那么这意味着它试图获得锁但它已经在其他地方获得了。但如果蜂拥而至LOCK_NB
返回 false 且 willblock=0 那么这意味着发生了非常糟糕的事情,flock 甚至没有考虑等待获得锁,因为这是完全不可能的。
查看这段代码(这也是一个要点 https://gist.github.com/arvenil/a1d273f5287ce8e1f4df):
<?php
// Let's create /tmp/ninja-lock1.txt ...
$fp0 = fopen('/tmp/ninja-lock1.txt', 'c');
// ... and close it imiedietly
fclose($fp0);
// File handler $fp0 was closed so flock()
// is unable to use it to gain lock.
// It will fail with wouldblock set to 0
// as it doesn't make sense to wait on unusable file handle.
//
// BTW flock() throws in such case warning "x is not a valid stream resource".
// Just for the purpose of clear output from this example
// I've suppressed it with @ - don't use @ in production
$flockResult = @flock($fp0, LOCK_EX | LOCK_NB, $wouldblock);
printf("flock=%b; wouldblock=%d (Acquire lock: %s)\n", $flockResult, $wouldblock, "failure, broken file handle");
// Two handlers for /tmp/ninja-lock2.txt
// to show flock() blocking result.
$fp1 = fopen('/tmp/ninja-lock2.txt', 'c');
$fp2 = fopen('/tmp/ninja-lock2.txt', 'c');
// Nobody is locking on /tmp/ninja-lock2.txt,
// so it will acquire lock and wouldblock will be 0
$flockResult = flock($fp1, LOCK_EX | LOCK_NB, $wouldblock);
printf("flock=%b; wouldblock=%d (Acquire lock: %s)\n", $flockResult, $wouldblock, "success");
// File is locked on $fp1 handle so flock won't acquire lock
// and wouldblock will be 1
$flockResult = flock($fp2, LOCK_EX | LOCK_NB, $wouldblock);
printf("flock=%b; wouldblock=%d (Acquire lock: %s)\n", $flockResult, $wouldblock, "failure, already acquired somewhere else");
// Result:
// $ php flock.php
// flock=0; wouldblock=0 (Acquire lock: failure, broken file handle)
// flock=1; wouldblock=0 (Acquire lock: success)
// flock=0; wouldblock=1 (Acquire lock: failure, already acquired somewhere else)
?>
另外,为了消除未来读者的任何困惑,值得注意的是,检查EWOULDBLOCK
只对flock()有意义LOCK_NB
标志,就像在阻塞模式下一样,它要么成功并阻塞,要么失败但不阻塞。
您可以通过查看来确认这一点群的 php 源代码 https://github.com/php/php-src/blob/af6c11c5f060870d052a2b765dc634d9e47d0f18/ext/standard/flock_compat.c#L48:
PHPAPI int php_flock(int fd, int operation)
#if HAVE_STRUCT_FLOCK /* {{{ */
{
struct flock flck;
int ret;
flck.l_start = flck.l_len = 0;
flck.l_whence = SEEK_SET;
if (operation & LOCK_SH)
flck.l_type = F_RDLCK;
else if (operation & LOCK_EX)
flck.l_type = F_WRLCK;
else if (operation & LOCK_UN)
flck.l_type = F_UNLCK;
else {
errno = EINVAL;
return -1;
}
ret = fcntl(fd, operation & LOCK_NB ? F_SETLK : F_SETLKW, &flck);
if (operation & LOCK_NB && ret == -1 &&
(errno == EACCES || errno == EAGAIN))
errno = EWOULDBLOCK;
if (ret != -1) ret = 0;
return ret;
}
EWOULDBLOCK
仅当operation & LOCK_NB && ret == -1 &&
(errno == EACCES || errno == EAGAIN)
.
如果您对实施更感兴趣,您还可以阅读fcntl 的手册页 http://www.linuxmanpages.com/man2/fcntl.2.php,大部分是关于F_SETLK
and F_SETLKW
:
F_SETLK
对 l_whence 指定的字节获取锁(当 l_type 为 F_RDLCK 或 F_WRLCK 时)或释放锁(当 l_type 为 F_UNLCK 时),
锁的 l_start 和 l_len 字段。如果冲突的锁被持有
另一个进程,此调用返回 -1 并将 errno 设置为 EACCES 或
再次。
F_SETLKW
与 F_SETLK 一样,但如果文件上持有冲突锁,则等待该锁被释放。如果捕获到信号
等待,然后调用被中断并且(在信号处理程序之后
已返回)立即返回(返回值 -1 并设置 errno
至 EINTR)。