php的多进程处理依赖于pcntl扩展,通过pcntl_fork创建子进程来进行并行处理。
	例1如下:
	
		
			?
		
			
				
					| 
						 
							1 
						
							2 
						
							3 
						
							4 
						
							5 
						
							6 
						
							7 
						
							8 
						
							9 
						
							10 
						
							11 
						
							12 
						
							13 
						
							14 
						
							15 
						
							16 
						
							17 
					 | 
					
						
							
								<?php 
							
								$pid = pcntl_fork(); 
							
								  
							
								if($pid == -1) { 
							
								   
							
								  die('fork error'); 
							
								} else if ($pid) { 
							
								   
							
								  echo "parent \n"; 
							
								   
							
								  pcntl_wait($status); 
							
								} else { 
							
								   
							
								  echo "child \n"; 
							
								  
							
								  exit; 
							
								} 
						 
					 | 
				
			
		
	 
 
	pcntl_fork创建了子进程,父进程和子进程都继续向下执行,而不同是父进程会获取子进程的$pid也就是$pid不为零。而子进程会获取$pid为零。通过if else语句判断$pid我们就可以在指定位置写上不同的逻辑代码。
	上述代码会分别输出parent和child。那么输出的parent和child是否会有顺序之分?是父进程会先执行?
	例2如下:
	
		
			?
		
			
				
					| 
						 
							1 
						
							2 
						
							3 
						
							4 
						
							5 
						
							6 
						
							7 
						
							8 
						
							9 
						
							10 
						
							11 
						
							12 
						
							13 
						
							14 
					 | 
					
						
							
								<?php 
							
								$pid = pcntl_fork(); 
							
								  
							
								if($pid == -1) { 
							
								  die('fork error'); 
							
								} else if ($pid) { 
							
								  sleep(3); 
							
								  echo "parent \n"; 
							
								  pcntl_wait($status); 
							
								} else { 
							
								  echo "child \n"; 
							
								  
							
								  exit; 
							
								} 
						 
					 | 
				
			
		
	 
 
	我们在父进程中通过sleep来延缓执行,看看效果。
	结果是,很快输出了child,等待了接近3秒后,才输出parent。所以父进程和子进程的执行是相对独立的,没有先后之分。
	那么问题又来了?pcntl_wait是做什么用的?
	会挂起当前进程,直到子进程退出,如果子进程在调用此函数之前就已退出,此函数会立刻返回。子进程使用的资源将被释放。
	例3如下:
	
		
			?
		
			
				
					| 
						 
							1 
						
							2 
						
							3 
						
							4 
						
							5 
						
							6 
						
							7 
						
							8 
						
							9 
						
							10 
						
							11 
						
							12 
						
							13 
						
							14 
					 | 
					
						
							
								<?php 
							
								$pid = pcntl_fork(); 
							
								  
							
								if($pid == -1) { 
							
								  die('fork error'); 
							
								} else if ($pid) { 
							
								  pcntl_wait ($status); 
							
								  echo "parent \n"; 
							
								} else { 
							
								  sleep(3); 
							
								  echo "child \n"; 
							
								  
							
								  exit; 
							
								} 
						 
					 | 
				
			
		
	 
 
	上述代码,我们可以看到,父进程执行pcntl_wait时就已经挂起,直到等待3秒后输出child,子进程退出后。父进程继续执行,输出parent。
	例4如下:
	
		
			?
		
			
				
					| 
						 
							1 
						
							2 
						
							3 
						
							4 
						
							5 
						
							6 
						
							7 
						
							8 
						
							9 
						
							10 
						
							11 
						
							12 
						
							13 
						
							14 
						
							15 
						
							16 
						
							17 
						
							18 
					 | 
					
						
							
								<?php 
							
								define('FORK_NUMS', 3); 
							
								  
							
								$pids = array(); 
							
								  
							
								for($i = 0; $i < FORK_NUMS; ++$i) { 
							
								  $pids[$i] = pcntl_fork(); 
							
								  if($pids[$i] == -1) { 
							
								    die('fork error'); 
							
								  } else if ($pids[$i]) { 
							
								    pcntl_waitpid($pids[$i], $status); 
							
								    echo "pernet \n"; 
							
								  } else { 
							
								    sleep(3); 
							
								    echo "child id:" . getmypid() . " \n"; 
							
								    exit; 
							
								  } 
							
								} 
						 
					 | 
				
			
		
	 
 
	上述代码,我们创建3个子进程,父进程分别挂起等待子进程结束后,输出parent。
	输出结果如下:
	
		child id:19090
		pernet
		child id:19091
		pernet
		child id:19092
		pernet
	例5如下:
	
		
			?
		
			
				
					| 
						 
							1 
						
							2 
						
							3 
						
							4 
						
							5 
						
							6 
						
							7 
						
							8 
						
							9 
						
							10 
						
							11 
						
							12 
						
							13 
						
							14 
						
							15 
						
							16 
						
							17 
						
							18 
						
							19 
						
							20 
						
							21 
						
							22 
						
							23 
						
							24 
					 | 
					
						
							
								<?php 
							
								define('FORK_NUMS', 3); 
							
								  
							
								$pids = array(); 
							
								  
							
								for($i = 0; $i < FORK_NUMS; ++$i) { 
							
								  $pids[$i] = pcntl_fork(); 
							
								  if($pids[$i] == -1) { 
							
								    die('fork error'); 
							
								  } else if ($pids[$i]) { 
							
								  
							
								  } else { 
							
								    sleep(3); 
							
								    echo "child id:" . getmypid() . " \n"; 
							
								    exit; 
							
								  } 
							
								} 
							
								  
							
								foreach($pids as $k => $v) { 
							
								  if($v) { 
							
								    pcntl_waitpid($v, $status); 
							
								    echo "parent \n"; 
							
								  } 
							
								} 
						 
					 | 
				
			
		
	 
 
	输出结果如下:
	
		child id:19118
		child id:19119
		child id:19120
		parent
		parent
		parent
	为什么上述代码跟例4的输出结果不一样?
	我们可以看到例5的pcntl_waitpid函数放在了foreach中,foreach代码是在主进程中,也就是父进程的代码中。当执行foreach时,可能子进程已经全部执行完毕并退出。pcntl_waitpid会立刻返回,连续输出三个parent。
	(*在子进程中,需通过exit来退出,不然会产生递归多进程,父进程中不需要exit,不然会中断多进程。)
	例6如下:
	
		
			?
		
			
				
					| 
						 
							1 
						
							2 
						
							3 
						
							4 
						
							5 
						
							6 
						
							7 
						
							8 
						
							9 
						
							10 
						
							11 
						
							12 
						
							13 
						
							14 
						
							15 
						
							16 
						
							17 
						
							18 
						
							19 
						
							20 
						
							21 
						
							22 
						
							23 
						
							24 
						
							25 
						
							26 
						
							27 
						
							28 
						
							29 
						
							30 
						
							31 
						
							32 
						
							33 
						
							34 
						
							35 
						
							36 
					 | 
					
						
							
								<?php 
							
								define('FORK_NUMS', 3); 
							
								  
							
								$pids = array(); 
							
								  
							
								$fp = fopen('./test.log', 'wb'); 
							
								$num = 1; 
							
								  
							
								for($i = 0; $i < FORK_NUMS; ++$i) { 
							
								  $pids[$i] = pcntl_fork(); 
							
								  if($pids[$i] == -1) { 
							
								    die('fork error'); 
							
								  } else if ($pids[$i]) { 
							
								  
							
								  
							
								  } else { 
							
								    for($i = 0; $i < 5; ++$i) { 
							
								  
							
								      flock($fp, LOCK_EX); 
							
								      fwrite($fp, getmypid() . ' : ' . date('Y-m-d H:i:s') . " : {$num} \r\n"); 
							
								  
							
								      flock($fp, LOCK_UN); 
							
								      echo getmypid(), ": success \r\n"; 
							
								      ++$num; 
							
								    } 
							
								    exit; 
							
								  } 
							
								} 
							
								  
							
								foreach($pids as $k => $v) { 
							
								  if($v) { 
							
								    pcntl_waitpid($v, $status); 
							
								  } 
							
								} 
							
								  
							
								fclose($fp); 
						 
					 | 
				
			
		
	 
 
	代码如上:我们创建三个子进程,来同时向test.log文件写入内容,test.log内容如下:
	
		19507 : 2016-03-16 20:40:52 : 1
		19507 : 2016-03-16 20:40:52 : 2
		19507 : 2016-03-16 20:40:52 : 3
		19507 : 2016-03-16 20:40:52 : 4
		19507 : 2016-03-16 20:40:52 : 5
		19509 : 2016-03-16 20:40:52 : 1
		19509 : 2016-03-16 20:40:52 : 2
		19509 : 2016-03-16 20:40:52 : 3
		19509 : 2016-03-16 20:40:52 : 4
		19509 : 2016-03-16 20:40:52 : 5
		19508 : 2016-03-16 20:40:52 : 1
		19508 : 2016-03-16 20:40:52 : 2
		19508 : 2016-03-16 20:40:52 : 3
		19508 : 2016-03-16 20:40:52 : 4
		19508 : 2016-03-16 20:40:52 : 5
	我们可以看到三个子进程的pid,它们分别执行了5次,时间几乎是在同时。但是$num的值并没像我们期望的那样从1-15进行递增。子进程中的变量是各自独立的,互不影响。子进程会自动复制父进程空间里的变量。
	如何在进程中共享数据?
	我们通过php的共享内存函数shmop来实现。
	
		
			?
		
			
				
					| 
						 
							1 
						
							2 
						
							3 
						
							4 
						
							5 
						
							6 
						
							7 
						
							8 
						
							9 
						
							10 
						
							11 
						
							12 
						
							13 
						
							14 
						
							15 
						
							16 
						
							17 
						
							18 
						
							19 
						
							20 
						
							21 
						
							22 
						
							23 
						
							24 
						
							25 
						
							26 
						
							27 
						
							28 
						
							29 
						
							30 
						
							31 
						
							32 
						
							33 
						
							34 
						
							35 
						
							36 
						
							37 
						
							38 
						
							39 
						
							40 
						
							41 
						
							42 
						
							43 
						
							44 
						
							45 
					 | 
					
						
							
								<?php 
							
								define('FORK_NUMS', 3); 
							
								  
							
								$pids = array(); 
							
								  
							
								$fp = fopen('./test.log', 'wb'); 
							
								$num = 1; 
							
								 
							
								$shmKey = 123; 
							
								 
							
								$shmId = shmop_open($shmKey, 'c', 0777, 64); 
							
								 
							
								shmop_write($shmId, $num, 0); 
							
								  
							
								for($i = 0; $i < FORK_NUMS; ++$i) { 
							
								  $pids[$i] = pcntl_fork(); 
							
								  if($pids[$i] == -1) { 
							
								    die('fork error'); 
							
								  } else if ($pids[$i]) { 
							
								  
							
								     
							
								  
							
								     
							
								    pcntl_waitpid($pids[$i], $status); 
							
								  } else { 
							
								     
							
								    $num = shmop_read($shmId, 0, 64); 
							
								    for($i = 0; $i < 5; ++$i) { 
							
								      fwrite($fp, getmypid() . ' : ' . date('Y-m-d H:i:s') . " : {$num} \r\n"); 
							
								      echo getmypid(), ": success \r\n"; 
							
								       
							
								      $num = intval($num) + 1; 
							
								    } 
							
								  
							
								     
							
								  
							
								    shmop_write($shmId, $num, 0); 
							
								    exit; 
							
								  } 
							
								} 
							
								  
							
								 
							
								shmop_delete($shmId); 
							
								shmop_close($shmId); 
							
								fclose($fp); 
						 
					 | 
				
			
		
	 
 
	上述代码的运行结果如下:
	
		19923 : 2016-03-17 00:05:18 : 1
		19923 : 2016-03-17 00:05:18 : 2
		19923 : 2016-03-17 00:05:18 : 3
		19923 : 2016-03-17 00:05:18 : 4
		19923 : 2016-03-17 00:05:18 : 5
		19924 : 2016-03-17 00:05:18 : 6
		19924 : 2016-03-17 00:05:18 : 7
		19924 : 2016-03-17 00:05:18 : 8
		19924 : 2016-03-17 00:05:18 : 9
		19924 : 2016-03-17 00:05:18 : 10
		19925 : 2016-03-17 00:05:18 : 11
		19925 : 2016-03-17 00:05:18 : 12
		19925 : 2016-03-17 00:05:18 : 13
		19925 : 2016-03-17 00:05:18 : 14
		19925 : 2016-03-17 00:05:18 : 15
	这样我们就在进程间共享了$num的数据。