多进程

php 的多进程实践

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

    //错误处理:创建子进程失败时返回-1.

    die('fork error');

else if ($pid) {

    //父进程会得到子进程号,所以这里是父进程执行的逻辑

    echo "parent \n";

    //等待子进程中断,防止子进程成为僵尸进程。

    pcntl_wait($status);

else {

    //子进程得到的$pid为0, 所以这里是子进程执行的逻辑。

    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。

输出结果如下:

1

2

3

4

5

6

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";

    }

}

输出结果如下:

1

2

3

4

5

6

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

37

<?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($fpgetmypid() . ' : ' 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内容如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

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

46

<?php

 

define('FORK_NUMS', 3);

 

$pids array();

 

$fp fopen('./test.log''wb');

$num = 1;

//共享内存段的key

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

 

        //阻塞,等待子进程退出

 

        //注意这里,如果是非阻塞的话,$num的计数会出现问题。

        pcntl_waitpid($pids[$i], $status);

    else {

        //读取共享内存段中的数据

        $num = shmop_read($shmId, 0, 64);

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

            fwrite($fpgetmypid() . ' : ' date('Y-m-d H:i:s') . " : {$num} \r\n");

            echo getmypid(), ": success \r\n";

            //递增$num

            $num intval($num) + 1;

        }

 

        //写入到共享内存段中

 

        shmop_write($shmId$num, 0);

        exit;

    }

}

 

//shmop_delete不会实际删除该内存段,它将该内存段标记为删除。

shmop_delete($shmId);

shmop_close($shmId);

fclose($fp);

上述代码的运行结果如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

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的数据。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值