php - 非阻塞 flock 函数的返回值与 $wouldblock 参数之间的区别?

标签 php concurrency flock

我正在尝试理解非阻塞 flock和 wouldblock 参数

$fp = fopen('/tmp/lock.txt', 'r+'); 
if(flock($fp, LOCK_EX | LOCK_NB, $wouldblock)) {
  echo 'Lock obtained';
}
else{
  echo 'Unable to obtain lock';
}
fclose($fp);

关于 wouldblock 的文档说:

The optional third argument is set to 1 if the lock would block (EWOULDBLOCK errno condition).

在测试环境中重现并发条件,如果另一个进程获得了锁,flock 函数将立即返回 FALSE(非阻塞)

那么,如果非阻塞模式下 flock 函数的返回值 allready 告诉我无法获得锁,我为什么还要关心 $wouldblock 值呢?

我无法区分 flock 函数返回 FALSE 和 $wouldblock 参数设置为 1 之间的区别,以及 $wouldblock 参数的用途。

最佳答案

这是因为 flock() 可能会失败,而不仅仅是因为已经在其他地方获得了锁。 在这种情况下,它不会阻塞等待锁被释放,但它会立即返回 false。换句话说,对于 LOCK_NB,如果 flock 返回 false 并且 wouldblock=1 那么这意味着,它试图获得锁定,但它已经在其他地方获得。但是,如果带有 LOCK_NB 的 flock 返回 false 并且 wouldblock=0 那么这意味着发生了非常糟糕的事情并且 flock 甚至没有考虑等待获得锁定,因为这是完全不可能的。

检查这段代码(here is also a gist):

<?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)
?>

另外,为了消除 future 读者的任何困惑,值得注意的是,检查 EWOULDBLOCK 仅对带有 LOCK_NB 标志的 flock() 有意义,因为在阻塞模式下它是成功并阻止或失败且不阻止。

您可以通过查看 php source code for flock 来确认这一点:

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)

如果您对实现更感兴趣,您还可以阅读 man page of fcntl ,主要是关于 F_SETLKF_SETLKW 的部分:

F_SETLK

Acquire a lock (when l_type is F_RDLCK or F_WRLCK) or release a lock (when l_type is F_UNLCK) on the bytes specified by the l_whence, l_start, and l_len fields of lock. If a conflicting lock is held by another process, this call returns -1 and sets errno to EACCES or EAGAIN.

F_SETLKW

As for F_SETLK, but if a conflicting lock is held on the file, then wait for that lock to be released. If a signal is caught while waiting, then the call is interrupted and (after the signal handler has returned) returns immediately (with return value -1 and errno set to EINTR).

关于php - 非阻塞 flock 函数的返回值与 $wouldblock 参数之间的区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24425247/

相关文章:

php - 如何在单个 div 中隔离 php 查询

java - 哈希表、ConcurrentHashMap 和数据可见性

asp.net - ASP.Net静态对象

java - 如何确定硬件线程数

php - LOCK_NB 忽略

php - redbean php 错误 - RedBeanPHP\OODBBean 声明 :offsetGet() must be compatible with that of ArrayAccess:offsetGet()

php - 我想使用 php 选择包含特定字符的特定单词列表

php - Mysql 我正在合并表。但我不能做过滤排序

c - flock(),然后是 fgets() : low-level locks,,然后是 stdio 读/写库函数。是否可以?

c - flock() : is it possible to merely check if the file is already locked, 如果没有实际获取锁?