forked from elarity/advanced-php
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
elarity
committed
Apr 25, 2018
1 parent
51b1c8d
commit 267adad
Showing
1 changed file
with
93 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
实际上PHP是有多线程的,只是很多人不常用。使用PHP的多线程首先需要下载安装一个线程安全版本(ZTS版本)的PHP,然后再安装pecl的[pthread扩展](http://pecl.php.net/package/pthreads "pthread扩展")。 | ||
|
||
实际上PHP是有多进程的,有一些人再用,总体来说php的多进程还算凑合,只需要在安装PHP的时候开启pcntl模块(是不是跟UNIX中的fcntl有点儿.... ....)即可。在*NIX下,在终端命令行下使用php -m就可以看到是否开启了pcntl模块。 | ||
|
||
所以我们只说php的多进程,至于php多线程就暂时放到一边儿。 | ||
|
||
**注意:不要在apache或者fpm环境下使用php多进程,这将会产生不可预估的后果。** | ||
|
||
**进程是程序执行的实例**,举个例子有个程序叫做 “ 病毒.exe ”,这个程序平时是以文件形式存储在硬盘上,当你双击运行后,就会形成一个该程序的进程。系统会给每一个进程分配一个唯一的非负整数用来标记进程,这个数字称作进程ID。当该进程被杀死或终止后,其进程ID就会被系统回收,然后分配给新的其余的进程。 | ||
|
||
说了这么多,这鬼东西有什么用吗?我平时用CI、YII写个CURD跟这个也没啥关联啊。实际上,如果你了解APACHE PHP MOD或者FPM就知道这些东西就是多进程实现的。以FPM为例,一般都是nginx作为http服务器挡在最前面,静态文件请求则nginx自行处理,遇到php动态请求则转发给php-fpm进程来处理。如果你的php-fpm配置只开了5个进程,如果处理任意一个用户的请求都需要1秒钟,那么5个fpm进程1秒中就最多只能处5个用户的请求。所以结论就是:如果要单位时间内干活更快更多,就需要更多的进程,总之一句话就是多进程可以加快任务处理速度。 | ||
|
||
在php中我们使用pcntl_fork()来创建多进程(在*NIX系统的C语言编程中,已有进程通过调用fork函数来产生新的进程)。fork出来新进程则成为子进程,原进程则成为父进程,子进程拥有父进程的副本。这里要注意: | ||
- 子进程与父进程共享程序正文段 | ||
- 子进程拥有父进程的数据空间和堆、栈的副本,注意是副本,不是共享 | ||
- 父进程和子进程将继续执行fork之后的程序代码 | ||
- fork之后,是父进程先执行还是子进程先执行无法确认,取决于系统调度(取决于信仰) | ||
|
||
这里说子进程拥有父进程数据空间以及堆、栈的副本,实际上,在大多数的实现中也并不是真正的完全副本。更多是采用了COW(Copy On Write)即写时复制的技术来节约存储空间。简单来说,如果父进程和子进程都不修改这些 数据、堆、栈 的话,那么父进程和子进程则是暂时共享同一份 数据、堆、栈。只有当父进程或者子进程试图对 数据、堆、栈 进行修改的时候,才会产生复制操作,这就叫做写时复制。 | ||
|
||
在调用完pcntl_fork()后,该函数会返回两个值。在父进程中返回子进程的进程ID,在子进程内部本身返回数字0。由于多进程在apache或者fpm环境下无法正常运行,所以大家一定要在php cli环境下执行下面php代码。 | ||
|
||
第一段代码,我们来说明在程序从pcntl_fork()后父进程和子进程将各自继续往下执行代码: | ||
```php | ||
<?php | ||
$pid = pcntl_fork(); | ||
if( $pid > 0 ){ | ||
echo "我是父亲".PHP_EOL; | ||
} else if( 0 == $pid ) { | ||
echo "我是儿子".PHP_EOL; | ||
} else { | ||
echo "fork失败".PHP_EOL; | ||
} | ||
``` | ||
将文件保存为test.php,然后在使用cli执行,结果如下图所示: | ||
![](https://static.ti-node.com/6374508376738496512) | ||
|
||
第二段代码,用来说明子进程拥有父进程的数据副本,而并不是共享: | ||
```php | ||
<?php | ||
// 初始化一个 number变量 数值为1 | ||
$number = 1; | ||
$pid = pcntl_fork(); | ||
if( $pid > 0 ){ | ||
$number += 1; | ||
echo "我是父亲,number+1 : { $number }".PHP_EOL; | ||
} else if( 0 == $pid ) { | ||
$number += 2; | ||
echo "我是儿子,number+2 : { $number }".PHP_EOL; | ||
} else { | ||
echo "fork失败".PHP_EOL; | ||
} | ||
``` | ||
![](https://static.ti-node.com/6374520918680535040) | ||
|
||
第三段代码,比较容易让人思维混乱,pcntl_fork()配合for循环来做些东西,问题来了:会显示几次 “ 儿子 ”? | ||
```php | ||
<?php | ||
for( $i = 1; $i <= 3 ; $i++ ){ | ||
$pid = pcntl_fork(); | ||
if( $pid > 0 ){ | ||
// do nothing ... | ||
} else if( 0 == $pid ){ | ||
echo "儿子".PHP_EOL; | ||
} | ||
} | ||
``` | ||
上面代码执行结果如下: | ||
![](https://static.ti-node.com/6374530342694420480) | ||
|
||
仔细数数,竟然是显示了7次 “ 儿子 ”。好奇怪,难道不是3次吗?... ... | ||
下面我修改一下代码,结合下面的代码,再思考一下为什么会产生7次而不是3次。 | ||
```php | ||
<?php | ||
for( $i = 1; $i <= 3 ; $i++ ){ | ||
$pid = pcntl_fork(); | ||
if( $pid > 0 ){ | ||
// do nothing ... | ||
} else if( 0 == $pid ){ | ||
echo "儿子".PHP_EOL; | ||
exit; | ||
} | ||
} | ||
``` | ||
执行结果如下图所示: | ||
![](https://static.ti-node.com/6374530960842555392) | ||
|
||
前面强调过:**父进程和子进程将继续执行fork之后的程序代码**。这里就不解释,实在想不明白的,可以动手自己画画思考一下。 | ||
|
||
为了避免写成臭尾理论文儿,这里强行断篇分割一下,下一章说僵尸进程和孤儿进程的一些恩怨情仇。 | ||
|
||
----- | ||
感谢workerman1群成员“小菜鸟”(不是我叫TA小菜鸟,是TA昵称就是小菜鸟)指出错误~ |