PHP-linux多进程

子进程的创建
一般的子进程的写法是:

?

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('could not fork');

}

else{

    if($pid){

        //从这里开始写的代码是父进程的

        exit("parent!");

    }

    else{

        //子进程代码,为防止不停的启用子进程造成系统资源被耗尽的情况,一般子进程代码运行完成后,加入exit来确保子进程正常退出。

        exit("child");

    }

}

?>

    上边的代码如果创建子进程成功的话,系统就有了2个进程,一个为父进程,一个为子进程,子进程的id号为$pid。在系统运行到$pid = pcntl_fork();时,在这个地方进行分支,父子进程各自开始运行各自的程序代码。代码的运行结果是parent 和child,很奇怪吧,为什么一个if和else互斥的代码中,都输出了结果?其实是像上边所说的,代码在pcntl_fork时,一个父进程运行parent,一个子进程运行了child。在代码结果上就显示了parent和child。至于谁先谁后的问题,这得要看系统资源的分配了。

    如果需要起多个进程来处理数据,可以根据数据的数量,按照约定好的数量比如说1000条一个进程来起子进程。使用for循环就可以了。   

?

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

46

#如果获得的总数小于或等于0,等待60秒,并退出

 if ($count <= 0)

 {

   sleep(60);

   exit;

 }

 #如果大于1000,计算需要起的进程数

 if ($count > 1000)

 {

   $cycleSize = ceil($count/1000);

 }

 else

 {

   $cycleSize = 1;

 }

  

 for ($i=0; $i<$cycleSize; $i++)

 {

   $pid  = pcntl_fork();

   if($pid == -1)

   {

     break;

   }

   else

   {

     if($pid)

     {

       #父进程获得子进程的pid,存入数组

       $pidArr[] = $pid;

     }

     else

     {

       //开始发送,子进程执行完自己的任务后,退出。

         exit;

     }

   }

 }

  

 while(count($pidArr) > 0)

 {

   $myId  = pcntl_waitpid(-1, $status, WNOHANG);

   foreach($pidArr as $key => $pid)

   {

     if($myId == $pid) unset($pidArr[$key]);

   }

 }

    然后使用crontab,来使此PHP程序每隔一段时间自动执行。

    当然,示例代码比较简单,具体还需要考虑怎么防止多个子进程执行到同一条数据或者当前进程处理数据未完成时,crontab又开始执行PHP文件启用新的进程等等。


PHP多进程实现方式
下面来系统地整理一下PHP多进程的实现方式:

1. 直接方式

pcntl_fork() 创建一个进程,在父进程返回值是子进程的pid,在子进程返回值是0,-1表示创建进程失败。跟C非常相似。

测试脚本 test.php
 

?

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

<?php

  // example of multiple processes

  date_default_timezone_set( 'Asia/Chongqing');

  echo "parent start, pid ", getmypid(), "\n" ;

  beep();

  for ($i=0; $i<3; ++$i){

     $pid = pcntl_fork();

      if ($pid == -1){

         die ("cannot fork" );

     } else if ($pid > 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<5; ++$j){

           beep();

        }

         exit ;

     }

  }

  // ***

  function beep(){

      echo getmypid(), "\t" , date( 'Y-m-d H:i:s', time()), "\n" ;

     sleep(1);

  }

?>

用命令行运行

?

1

#php -f test.php

输出结果

?

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

parent start, pid 1793

1793  2013-01-14 15:04:17

parent continue

1793  2013-01-14 15:04:18

child start, pid 1794

1794  2013-01-14 15:04:18

1794  2013-01-14 15:04:19

1793  2013-01-14 15:04:19

1794  2013-01-14 15:04:20

parent continue

1793  2013-01-14 15:04:20

child start, pid 1795

1795  2013-01-14 15:04:20

17931794        2013-01-14 15:04:212013-01-14 15:04:21

 

1795  2013-01-14 15:04:21

1794  2013-01-14 15:04:22

1795  2013-01-14 15:04:22

parent continue

1793  2013-01-14 15:04:22

child start, pid 1796

1796  2013-01-14 15:04:22

1793  2013-01-14 15:04:23

1796  2013-01-14 15:04:23

1795  2013-01-14 15:04:23

1795  2013-01-14 15:04:24

1796  2013-01-14 15:04:24

1796  2013-01-14 15:04:25

1796  2013-01-14 15:04:26

从中看到,创建了3个子进程,和父进程一起并行运行。其中有一行格式跟其他有些不同,
17931794                2013-01-14 15:04:212013-01-14 15:04:21
因为两个进程同时进行写操作,造成了冲突。


2. 阻塞方式

用直接方式,父进程创建了子进程后,并没有等待子进程结束,而是继续运行。似乎这里看不到有什么问题。如果php脚本并不是运行完后自动结束,而是常驻内存的,就会造成子进程无法回收的问题。也就是僵尸进程。可以通过pcntl_wai()方法等待进程结束,然后回收已经结束的进程。
将测试脚本改成:
 

?

1

2

3

4

5

6

7

8

9

10

11

12

$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){

   ...

}

用命令行运行

?

1

#php -f test.php

输出结果

?

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

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()将自己阻塞,等待子进程运行完了才接着运行。


3. 非阻塞方式

阻塞方式失去了多进程的并行性。还有一种方法,既可以回收已经结束的子进程,又可以并行。这就是非阻塞的方式。
修改脚本:
 

?

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

<?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<3; ++$i){

     $pid = pcntl_fork();

      if ($pid == -1){

         die ("cannot fork" );

     } else if ($pid > 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<5; ++$j){

           beep();

        }

         exit (0);

     }

  }

  // parent

  while (1){

      // do something else

     sleep(5);

  }

  // ***

  function garbage($signal){

      echo "signel $signal received\n" ;

       

      while (($pid = pcntl_waitpid(-1, $status, WNOHANG))> 0){

         echo "\t child end pid $pid , status $status\n" ;

     }

  }

  function beep(){

      echo getmypid(), "\t" , date( 'Y-m-d H:i:s', time()), "\n" ;

     sleep(1);

  }

?>

用命令行运行

?

1

#php -f test.php &

输出结果

?

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

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 pts/1    00:00:00 php -f t5.php
是父进程,子进程被回收了。


子进程退出状态

?

1

pcntl_waitpid(-1, $status, WNOHANG) $status

 返回子进程的结束状态

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值