场景:统计从1月份到3月份,每一天的商品数量。
// 开始日期
$startDate = '2022-01-01';
// 结束日期
$endDate = '2022-03-31';
// 当前统计的日期 (从开始日期开始统计)
$currentDate = $startDate;
// 用于记录子进程id的容器
$childrenIds = [];
// 直到计算日期超过结束日期结束
while(strtotime($currentDate) <= strtotime($endDate)) {
// A: 从这里开始, 得到进程id
$pid = pcntl_fork();
// 从调用pcntl_fork()时, 系统会复制一份所有的代码, 分别给主进程和进程同时执行
// 那子进程从哪里开始执行? 从pcntl_fork() 开始往下执行
// 那父子进程的变量如$startDate, $endDate也是共享一样的吗, 是一样,因为是复制
// 那父子进程的变量修改,相互影响吗? 因为两个作用于不一样,所以还是要看是否按引用传递
if ($pid == -1) {
die('进程fork失败');
} elseif ($pid == 0) {
// B: 如果当前执行的是子进程, 由于不是父进程,所以拿到的$pid = 0, 所以由子进程执行sum统计操作
$this->sum($currentDate);
// 子进程仅负责统计, 不需要走下面的操作, 下面操作交给父进程处理
die();
} else {
// C: 父进程会拿到子进程的$pid, 所以 $pid > 0
$childrenIds[$pid] = $pid;
// 下个子进程的统计日期是+1天
$currentDate = date('Y-m-d', strtotime($currentDate.' +1 day'));
// D: 为了防止创建太多的子进程,父进程控制一下子进程数量
if (count($childrenIds) > 3) {
// 父进程先阻塞一下不动, 让已经创建的子进程统计完,再进行下次循环(走A的操作,创建新的子进程)
$childrenId = pcntl_wait($status);
// 减少子进程数量, 方便父进程执行到D那一窜代码,控制子进程数量
unset($childrenIds[$childrenId])
}
}
}
// 1. 下面的代码由于上面的while父进程没有die, 因此下面的代码都是在父进程执行
// 2. 所以这就为什么在while中子进程执行完统计,就需要die, 防止子进程执行了父进程的代码
// 3. 由于在上面while中,父进程控制子进程的数量超过3个才让子进程继续跑完,因此下面的代码是让剩余的3个子进程也要跑完,父进程确保所有子进程都跑完。
while(count($childrenIds) > 0) {
$childrenId = pcntl_wait($status);
unset($childrenIds[$childrenId])
}
注意地方:由于父子进程状态是一样的,所以子进程最好都需要重置一下数据库连接,redis连接,rabbitmq连接等