因为两个进程同时进行写操作,所以会产生冲突。
2. 封锁形式
采用直接方式,父进程创建子进程后,并不等待子进程结束,而是继续运行。 这里似乎没有问题。 如果php脚本运行后没有手动结束,而是常驻内存,就会导致子进程无法回收的问题。 即僵尸进程。 可以通过pcntl_wai()方法等待进程结束,然后回收已经结束的进程。
将测试脚本更改为:
$pid = pcntl_fork(); if ($pid == -1){ ... } else if ($pid > 0){ echo "parent continue n"; pcntl_wait($status); for ($k=0; $k<2; ++$k){ beep(); } } else if ($pid == 0){ ... }
从命令行运行
#php -f test.php
输出结果
parent start, pid 1807 1807 2013-01-14 15:20:05 parent continue child start, pid 1808 1808 2013-01-14 15:20:06 1808 2013-01-14 15:20:07 1808 2013-01-14 15:20:08 1808 2013-01-14 15:20:09 1808 2013-01-14 15:20:10 1807 2013-01-14 15:20:11 1807 2013-01-14 15:20:12 parent continue child start, pid 1809 1809 2013-01-14 15:20:13 1809 2013-01-14 15:20:14 1809 2013-01-14 15:20:15 1809 2013-01-14 15:20:16 1809 2013-01-14 15:20:17 1807 2013-01-14 15:20:18 1807 2013-01-14 15:20:19 child start, pid 1810 1810 2013-01-14 15:20:20 parent continue 1810 2013-01-14 15:20:21 1810 2013-01-14 15:20:22 1810 2013-01-14 15:20:23 1810 2013-01-14 15:20:24 1807 2013-01-14 15:20:25 1807 2013-01-14 15:20:26
父进程在 pcntl_wait() 中阻塞自身php 进程数php 进程数,等待子进程完成运行后再继续。
3.非阻塞形式
阻塞形式失去了多进程的并行性。 还有一种方法可以回收已经结束的子进程并并行执行它们。 这是非阻塞形式。
修改脚本:
<?php // example of multiple processes date_default_timezone_set( 'Asia/Chongqing'); declare (ticks = 1); pcntl_signal(SIGCHLD, "garbage" ); echo "parent start, pid ", getmypid(), "n" ; beep(); for ($i=0; $i 0){ echo "parent continue n"; for ($k=0; $k<2; ++$k){ beep(); } } else if ($pid == 0){ echo "child start, pid ", getmypid(), "n" ; for ($j=0; $j 0){ echo "t child end pid $pid , status $statusn" ; } } function beep(){ echo getmypid(), "t" , date( 'Y-m-d H:i:s', time()), "n" ; sleep(1); } ?>
从命令行运行
#php -f test.php &
输出结果
parent start, pid 2066 2066 2013-01-14 16:45:34 parent continue 2066 2013-01-14 16:45:35 child start, pid 2067 2067 2013-01-14 16:45:35 20662067 2013-01-14 16:45:362013-01-14 16:45:36 2067 2013-01-14 16:45:37 parent continue 2066 2013-01-14 16:45:37 child start, pid 2068 2068 2013-01-14 16:45:37 2067 2013-01-14 16:45:38 2068 2013-01-14 16:45:38 2066 2013-01-14 16:45:38 parent continue 2066 2013-01-14 16:45:40 child start, pid 2069 2069 2067 2013-01-14 16:45:40 2013-01-14 16:45:40 2068 2013-01-14 16:45:40 2066 2013-01-14 16:45:41 2069 2013-01-14 16:45:41 2068 2013-01-14 16:45:41 signel 17 received child end pid 2067, status 0 2069 2013-01-14 16:45:42 2068 2013-01-14 16:45:42 2069 2013-01-14 16:45:43 signel 17 received child end pid 2068, status 0 2069 2013-01-14 16:45:44 signel 17 received child end pid 2069, status 0
多个进程再次并行运行,运行10秒左右后,使用ps -ef | grep php 检查正在运行的进程。 只有一个过程。
lqling 2066 1388 0 16:45 点/1 00:00:00 php -f t5.php
就是父进程和子进程被回收了。
子进程退出状态
pcntl_waitpid(-1, $status, WNOHANG) $status
返回子进程的结束状态
windows下多线程
Windows系统不支持pcntl函数。 幸运的是,有curl_multi_exec()这个工具,它使用内部多线程访问多个链接,每个链接都可以作为一个任务。
编写脚本test1.php
$task){ $ch[$i] = curl_init(); curl_setopt($ch[$i], CURLOPT_URL, $task); curl_setopt($ch[$i], CURLOPT_RETURNTRANSFER, 1); curl_multi_add_handle($mh, $ch[$i]); } do {$mrc = curl_multi_exec($mh,$active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); while ($active && $mrc == CURLM_OK) { if (curl_multi_select($mh) != -1) { do {$mrc = curl_multi_exec($mh, $active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); } } // completed, checkout result foreach ($tasks as $j => $task){ if (curl_error($ch[$j])){ echo "task ${j} [$task ] error " , curl_error($ch[$j]), "rn" ; } else { echo "task ${j} [$task ] get: rn" , curl_multi_getcontent($ch[$j]), "rn" ; } } ?>
编写脚本test2.php
<?php date_default_timezone_set( 'Asia/Chongqing'); echo "child start, pid ", getmypid(), "rn" ; for ($i=0; $i
从命令行运行
#php -f test1.php &
输出结果
task 0 [http://localhost/feedbowl/t2.php?job=task1] get: child start, pid 5804 5804 2013-01-15 20:22:35 5804 2013-01-15 20:22:36 5804 2013-01-15 20:22:37 5804 2013-01-15 20:22:38 5804 2013-01-15 20:22:39 task 1 [http://localhost/feedbowl/t2.php?job=task2] get: child start, pid 5804 5804 2013-01-15 20:22:35 5804 2013-01-15 20:22:36 5804 2013-01-15 20:22:37 5804 2013-01-15 20:22:38 5804 2013-01-15 20:22:39 task 2 [http://localhost/feedbowl/t2.php?job=task3] get: child start, pid 5804 5804 2013-01-15 20:22:35 5804 2013-01-15 20:22:36 5804 2013-01-15 20:22:37 5804 2013-01-15 20:22:38 5804 2013-01-15 20:22:39
从复制的时间我们可以看到,多个任务几乎是同时运行的。