0

我想在对虚拟命名资源的 PHP 请求之间实现一种快速有效的序列化机制,当脚本完成时,无论是正常还是由于错误,它都会解锁。我过去有 eaccelerator_lock() 及其对应的 eaccelerator_unlock() ,但 eaccelerator 不再实现该功能。我想做的是:

lock_function("my-named-resource");
..
my_might_abort_abruptly_function();
..
unlock_function("my-named-resource");

其他使用完全相同参数调用 lock_function() 的 PHP 脚本应该阻塞,直到该脚本调用 unlock_function() 或中止。资源名称在处理之前是未知的(它是一个生成的字符串),并且不能被限制在一个小集合中(即锁定机制应该具有良好的粒度)。我想避免使用 try/catch 代码,因为在某些情况下不调用 catch。此外,应避免任何依赖于手动 usleep() 旋转(而不是本机操作系统阻塞)的机制。

我的是服务器中唯一正在运行的应用程序。该系统是带有 PHP 5.3.3、Apache 2.2.15 的 CentOS 6 Linux,我可以完全控制它。

我探索了以下替代方案:

  • 信号量:它们在 PHP 中没有很好地实现;Linux 允许数千个数组,而 PHP 只为每个 id 分配一个。

  • flock():我的资源是虚拟的,flock() 只会锁定整个/真实/现有文件;我需要预先创建数千个文件并选择一个用哈希函数锁定。粒度取决于文件的数量。

  • dio_fcntl():我可以尝试用单个文件和 fcntl(F_SETLK) 重现flock() 的想法。这将具有良好粒度而不需要许多文件的优点;该文件甚至可以是零字节长!(F_SETLK 可以锁定超出文件末尾)。唉! 问题是文档中没有任何地方说 dio_fcntl() 将在脚本终止时释放资源。

  • 数据库锁:我可以在具有良好键锁定粒度的数据库中实现一些键锁定,尽管这太依赖于数据库。也不会那么快。

  • 实现我自己的 PHP 扩展:我真的很想避免这条路。

问题是,我认为某个地方的某个人应该在我之前想到这一点。什么是好的选择?还有其他我没有看到的解决方案吗?

提前致谢。吉列尔莫。

4

3 回答 3

0

您总是可以在脚本启动时回到旧学校并触摸文件,并在完成时将其删除。

您可以register_shutdown_function删除文件。

文件的存在与否将指示资源的锁定状态。

于 2013-06-11T03:07:52.513 回答
0

事实证明 dio_open() 确实在脚本终止时释放资源。所以我最终编写了以下函数:

$lockfile = $writable_dir."/serialized.lock";

function serialize_access($name)
{
    $f = serialize_openfile();
    if( !$f ) return false;
    $h = serialize_gethash($name);
    return dio_fcntl($f, F_SETLKW, array("whence"=>SEEK_SET,"start"=>$h, "length"=>1, "type"=>F_WRLCK)) >= 0;
}

function serialize_release($name)
{
    $f = serialize_openfile();
    if( !$f ) return false;
    $h = serialize_gethash($name);
    @dio_fcntl($f, F_SETLK, array("whence"=>SEEK_SET,"start"=>$h, "length"=>1, "type"=>F_UNLCK));
}

function serialize_gethash($name)
{
    // Very good granularity (2^31)
    return crc32($name) & 0x7fffffff;
}

function serialize_openfile()
{
    global $lockfile, $serialize_file;

    if( !isset($serialize_file) )
    {
        $serialize_file = false;

        if( extension_loaded("dio") )
        {
            $serialize_file = @dio_open($lockfile,O_RDWR);
            if( $serialize_file )
            {
                // Do not attempt to create the file with dio_open()
                // because the file permissions get all mangled.

                $prev = umask(0);
                $temp = fopen($lockfile,"a");
                if( $temp )
                {
                    $serialize_file = @dio_open($lockfile,O_RDWR);
                    fclose($temp);
                }
                umask($prev);
            }
        }
    }

    return $serialize_file;
}

它似乎工作得很好。

于 2013-06-11T18:00:45.750 回答
0

实现我自己的 PHP 扩展

您可能想检查ninja-mutex库,它完全符合您的要求

于 2013-07-29T19:57:48.013 回答