主进程是原始启动脚本的进程,即执行 php my_processes.php
的那个进程。这个进程负责创建子进程,并等待它们完成。具体来说,主进程的作用包括:
- 创建子进程: 使用
pcntl_fork()
创建多个子进程。 - 收集子进程的PID: 将子进程的PID保存在数组中,以便稍后进行管理。
- 等待子进程完成: 使用
pcntl_waitpid()
等待所有子进程完成。
在代码中,主进程的逻辑如下:
# my_processes.php
<?php
$processes = 5; // 要创建的子进程数量
$pids = []; // 用于存储子进程的PID
for ($i = 0; $i < $processes; $i++) {
$pid = pcntl_fork(); // 创建子进程
if ($pid == -1) {
die('Could not fork'); // 创建子进程失败,退出
} else if ($pid) {
// 主进程执行的逻辑
$pids[] = $pid; // 将子进程的PID存储到数组中
} else {
// 子进程执行的逻辑
echo "Process $i is running\n";
// 模拟一些处理
sleep(2);
echo "Process $i has finished\n";
exit(0); // 子进程完成任务后退出
}
}
// 等待所有子进程完成
foreach ($pids as $pid_) {
echo "pid=".$pid_."\n ";
pcntl_waitpid($pid_, $status);
}
?>
详细解释
- pcntl_fork(): 创建一个新进程。返回值是:
- 负数:表示创建进程失败。
- 0:表示这是在子进程中执行的代码。
- 正数:表示这是在主进程中执行的代码,并且返回的是子进程的PID。
- 主进程:
- 当
pcntl_fork()
返回一个正数时,表示这是主进程执行的代码。主进程会将子进程的PID保存到$pids
数组中,并继续创建下一个子进程。 - 当所有子进程都创建完毕后,主进程会进入
foreach
循环,调用pcntl_waitpid()
等待每个子进程完成。
- 当
- 子进程:
- 当
pcntl_fork()
返回0时,表示这是子进程执行的代码。子进程会输出信息,模拟一些处理任务,然后调用exit(0)
退出。
- 当
通过这种方式,主进程负责创建和管理子进程,而每个子进程则执行自己的任务并在完成后退出。主进程最终等待所有子进程完成后,脚本执行完毕。
输出结果
Process 0 is running
Process 2 is running
pid=14965
Process 1 is running
Process 4 is running
Process 3 is running
Process 0 has finished
Process 2 has finished
Process 1 has finished
Process 3 has finished
Process 4 has finished
pid=14966
pid=14967
pid=14968
pid=14969
分析和理解
- 创建子进程:
- 主进程依次创建5个子进程。
- 每个子进程都会打印"Process i is running"并休眠2秒,然后打印"Process $i has finished"。
- 子进程并发执行:
- 子进程是并发执行的,因此"Process $i is running"的打印顺序可能不按顺序。具体的执行顺序取决于操作系统的调度策略。
- 你看到的 “Process 0 is running”、“Process 2 is running” 等是各个子进程在并发执行中的输出。
- 主进程等待子进程:
- 主进程创建完所有子进程后,会进入
foreach
循环,逐个等待子进程完成。 pcntl_waitpid($pid_, $status)
会阻塞主进程,直到对应的子进程完成。- 主进程在等待子进程时,先输出
pid
,然后等待子进程结束。
- 主进程创建完所有子进程后,会进入
- 输出顺序:
- 输出 “pid=14965” 等 PID 是在主进程等待每个子进程之前打印的。
- 由于子进程并发执行,主进程的等待和子进程的输出是交替进行的。
- 例如,“Process 0 is running” 和 “pid=14965” 之间并没有严格的先后顺序,取决于具体执行时的调度情况。
- 因此,你看到的 “Process $i is running” 和 “pid=” 的输出顺序并不一定严格对应。
总结
多进程编程中,子进程是并发执行的,它们的输出顺序可能交错。主进程会等待每个子进程完成,并在等待之前打印子进程的 PID。输出的顺序取决于操作系统的调度,并不一定按创建顺序或执行顺序严格对应。