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

时间:2014-06-26 07:57:07

标签: php concurrency flock

我试图了解非阻塞flock和willblock参数

$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:

  

如果锁定会阻塞(EWOULDBLOCK错误条件),则可选的第三个参数设置为1。

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

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

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

1 个答案:

答案 0 :(得分:9)

这是因为flock()可能会失败,因为锁已经在其他地方获得了。 在这种情况下,它不会阻止等待锁被释放,但它会立即返回false。换句话说,LOCK_NB如果flock返回false而willblock = 1那么意味着,它试图获得锁定,但它已经在其他地方被获取。但是如果flock LOCK_NB返回false并且willblock = 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)
?>

另外,为了清除未来读者的任何困惑,值得注意的是,检查EWOULDBLOCK只对含LOCK_NB标志的flock()有意义,因为在阻止模式下它是&#39;无论是成功还是阻止或失败,都没有阻止。

您可以通过查看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

     

获取锁(当l_type为F_RDLCK或F_WRLCK时)或释放锁(当l_type为F_UNLCK时)对l_whence指定的字节,   l_start和l_len字段的锁定。如果持有冲突的锁定   另一个进程,此调用返回-1并将errno设置为EACCES或   EAGAIN。

     

F_SETLKW

     

对于F_SETLK,但如果文件上存在冲突的锁,则等待释放该锁。如果发出信号的话   等待,然后呼叫被中断(在信号处理程序之后)   已返回)立即返回(返回值为-1且设置了errno   到EINTR)。