0
点赞
收藏
分享

微信扫一扫

php多进程编程进程间问题-谨慎处理共享资源的访问,避免竞态条件

php多进程编程进程间问题-谨慎处理共享资源的访问,避免竞态条件_子进程

hi, 大家好,今天分享一个常见多进程编程的基础bug问题。

问题

在多进程环境中使用 mkdir() 函数创建目录时可能会出现PHP Warning: mkdir(): File exists

  • 示例代码一:

$numberOfProcesses = 10;

for ($i = 0; $i < $numberOfProcesses; $i++) {
    $pid = pcntl_fork();
    if ($pid == -1) {
        // 处理创建进程失败的情况
        exit("Failed to create child process.");
    } elseif ($pid == 0) {
        // 子进程代码
        echo "Child process " . getmypid() . " started.\n";
        // 执行子进程的任务
        if (!is_dir('./test')) {
            mkdir('./test', 0777);
        }
        echo "Child process " . getmypid() . " finished.\n";
        exit(); // 子进程结束
    } else {
        // 父进程继续创建下一个子进程
        continue;
    }
}

// 等待所有子进程结束
while (pcntl_waitpid(0, $status) != -1) {
    $status = pcntl_wexitstatus($status);
    echo "Child process $status finished.\n";
}

  • 示例代码二:

$numberOfProcesses = 20;

for ($i = 0; $i < $numberOfProcesses; $i++) {
    $pid = pcntl_fork();
    if ($pid == -1) {
        // 处理创建进程失败的情况
        exit("Failed to create child process.");
    } elseif ($pid == 0) {
        // 子进程代码
        echo "Child process " . getmypid() . " started.\n";
        // 执行子进程的任务
        if (!is_dir('./test') && mkdir('./test', 0777)) 
        echo "Child process " . getmypid() . " finished.\n";
        exit(); // 子进程结束
    } else {
        // 父进程继续创建下一个子进程
        continue;
    }
}

// 等待所有子进程结束
while (pcntl_waitpid(0, $status) != -1) {
    $status = pcntl_wexitstatus($status);
    echo "Child process $status finished.\n";
}

产生原因

由于并发的特性,多个进程同时判断目录是否存在,然后尝试创建目录,可能会出现竞争条件。当一个进程判断目录不存在时,另一个进程可能在此之前已经创建了该目录,导致当前进程执行 mkdir() 函数时报错

解决办法

  • 使用锁(文件锁、分布式锁都可以,以文件锁为例):在创建目录前,使用文件锁对目标目录进行加锁,确保只有一个进程能够执行创建目录的操作。其他进程在获取锁之前会等待,避免并发问题。

$numberOfProcesses = 20;

for ($i = 0; $i < $numberOfProcesses; $i++) {
    $pid = pcntl_fork();
    if ($pid == -1) {
        // 处理创建进程失败的情况
        exit("Failed to create child process.");
    } elseif ($pid == 0) {
        // 子进程代码
        echo "Child process " . getmypid() . " started.\n";
        // 执行子进程的任务
        $lockFile = 'lockfile.lock';
        $lockHandle = fopen($lockFile, 'w');
        if (flock($lockHandle, LOCK_EX)) {
            if (!is_dir('./test2')) {
                mkdir('./test2', 0777);
            }

            flock($lockHandle, LOCK_UN);
        }

        fclose($lockHandle);

        echo "Child process " . getmypid() . " finished.\n";
        exit(); // 子进程结束
    } else {
        // 父进程继续创建下一个子进程
        continue;
    }
}

// 等待所有子进程结束
while (pcntl_waitpid(0, $status) != -1) {
    $status = pcntl_wexitstatus($status);
    echo "Child process $status finished.\n";
}

  • 将 is_dir() 和 mkdir() 写在一起是一个常见的做法,也是一种有效的解决方案。这种方式可以通过一个原子操作来判断目录是否存在并创建目录。
举报

相关推荐

0 条评论