php多进程编程进程间问题-谨慎处理共享资源的访问,避免竞态条件
时间:2023-10-16 21:58 作者:wanzi 分类: 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() 写在一起是一个常见的做法,也是一种有效的解决方案。这种方式可以通过一个原子操作来判断目录是否存在并创建目录。
标签: php