2

所以我有一个脚本用于接受和处理来自另一个脚本和/或应用程序的请求。但是,我的脚本必须完成的一项任务是为每个请求分配一个唯一的、连续的“ID”。

例如,假设应用程序 A 向我的脚本发出 1000 个请求,同时应用程序 B 向我的脚本发出 500 个请求。我必须给他们 1500 个唯一的序列号,比如 2001~3500 给他们每个人。

然而,它们之间的顺序无关紧要,所以我可以给它们这样的数字:

#2001 for 1st request from A (henceforth, A1)
#2002 for A2
#2003 for B1
#2004 for A3
#2005 for B2
...and so on...

我尝试创建一个存储该数字的文件和一个具有如下功能的单独锁定文件:

private function get_last_id()
{
    // Check if lock file exists...
    while (file_exists("LAST_ID_LOCKED")) {
        // Wait a little bit before checking again
        usleep(1000);
    }

    // Create the lock file
    touch("LAST_ID_LOCKED");

    // Create the ID file for the first time if required
    if (!file_exists("LAST_ID_INDICATOR")) {
        file_put_contents("LAST_ID_INDICATOR", 0);
    }

    // Get the last ID
    $last_id = file_get_contents("LAST_ID_INDICATOR");
    // Update the last ID
    file_put_contents("LAST_ID_INDICATOR", $last_id + 1);

    // Delete the lock file
    unlink("LAST_ID_LOCKED");

    return $last_id;
}

然而,这段代码会产生一个竞争条件,如果我向他们发送那些 1500 个请求,最后一个 ID 将有很多缺失,(例如,只达到 3211 而不是 3500)。

我也尝试过像这样使用羊群,但无济于事:

private function get_last_id()
{
    $f = fopen("LAST_ID_INDICATOR", "rw");

    while (true) {
        if (flock($f, LOCK_SH)) {
            $last_id = fread($f, 8192);
            flock($f, LOCK_UN);
            fclose($f);
            break;
        }
        usleep($this->config["waiting_time"]);
    }

    $f = fopen("LAST_ID_INDICATOR", "rw");

    while (true) {
        if (flock($f, LOCK_SH)) {
            $last_id = fread($f, 8192);
            $last_id++;
            ftruncate($f, 0);
            fwrite($f, $last_id);
            flock($f, LOCK_UN);
            fclose($f);
            break;
        }
        usleep($this->config["waiting_time"]);
    }

    return $last_id;
}

那么,我还能做些什么来寻找这种情况的解决方案呢?

注意:由于服务器限制,我仅限于 PHP 5.2,没有信号量之类的东西。

4

2 回答 2

0

如果您可以访问具有锁定功能的数据库,则可以使用它。例如,对于带有框架 PHP 代码的 MySQL:

  1. 创建一个包含一行和一列的表(如果您不想“双重使用”已经存在的表):

    $sql = '创建表TABLENAME(列名整数)引擎 = MyISAM';

    执行Sql($sql) ...

  2. 创建一个 PHP 函数来(重新)设置计数器/Id 值:

    $sql = '更新TABLENAMECOLUMNNAME=0';

    执行Sql($sql); ...

  3. 创建一个 PHP 函数来获取一个唯一的、连续的 id:

    $sql = "SELECT GET_LOCK('numberLock',10)";

    执行Sql($sql); ...

    $sql = '选择 * FROM TABLENAME';

    if ($result = mysqli_query($link, $sql)) {

        $row = mysqli_fetch_row($result);
    
        $wantedId = $row[0];
    
        // do something with the id ...
    
        mysqli_free_result($result);
    

    }

    $sql = '更新TABLENAMECOLUMNNAME= COLUMNNAME+1';

    执行Sql($sql); ...

    $sql = "SELECT RELEASE_LOCK('numberLock')";

    执行Sql($sql); ...

于 2013-10-21T15:15:29.823 回答
0

由于似乎没有人给出答案,我会给你一个可能的解决方案。

使用Lamport's Bakery 算法作为解决方案的一部分。

编辑:如果您不需要保留订单,过滤器锁定会更好。

显然,这将有其自身的挑战实现,但值得一试,如果你做对了,它可能只是你想做的事。

既然你提到了信号量,我假设你知道足够的知识来理解这个概念。

这可以在“多处理器编程的艺术”的第 2 章中找到。

于 2013-10-21T11:40:52.700 回答