PHP文件编写中的竞争条件

时间:2013-10-21 10:13:41

标签: php concurrency race-condition

所以我有一个脚本来接受和处理来自其他脚本和/或应用程序的请求。但是,我的脚本必须完成其中一项任务,即为每个请求分配一个唯一的,顺序的“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)。

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

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。

2 个答案:

答案 0 :(得分:0)

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

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

修改:如果您不需要保留订单,过滤器锁定功能会更好。

显然,这将有自己的挑战实施,但值得一试,如果你做对了,它可能只是为你想要做的事情。

既然你提到了信号量,我认为你已经掌握了足够的知识来理解这个概念。

这可以在“The art of multiprocessor programming”的第2章中找到。

答案 1 :(得分:0)

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

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

    $ sql ='CREATE TABLE TABLENAME(COLUMNNAME INTEGER)ENGINE = MyISAM';

    excecuteSql($ sql)...

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

    $ sql ='UPDATE TABLENAME SET COLUMNNAME = 0';

    的ExecuteSQL($ SQL); ...

  3. 创建一个PHP函数以获得唯一的连续id:

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

    的ExecuteSQL($ SQL); ...

    $ sql ='SELECT * 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 ='更新TABLENAME设置COLUMNNAME = COLUMNNAME + 1';

    的ExecuteSQL($ SQL); ...

    $ sql =“SELECT RELEASE_LOCK('numberLock')”;

    的ExecuteSQL($ SQL); ...