PHP协程内容的详解

来源:不言 发布时间:2018-12-15 15:54:29 阅读量:864

这篇文章给大家分享了关于php协成的内容,有一定的参考价值,希望可以帮助到有需要的朋友。

概念

咱们知道多进程和多线程是实现并发的有效方式。但多进程的上下文切换资源开销太大;多线程开销相比要小很多,也是现在主流的做法,但其的控制权在内核,从而使用户(程序员)失去了对代码的控制,而且线程的上下文切换也是有一定开销的。 这时为了解决以上问题,"协程"(coroutine)的概念就产生了。你可以将协程理解为更轻量级的线程。这种线程叫做“用户空间线程“。协程,有下面两个特点:

  1. 协同。因为是由程序员自己写的调度策略,其通过协作而不是抢占来进行切换

  2. 在用户态完成创建,切换和销毁

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

36

37

38

39

40

41

42

43

44

45

46

47

48

class MyIterator implements Iterator

{

    private $var = array();

 

    public function __construct($array)

    {

        if (is_array($array)) {

            $this->var = $array;

        }

    }

 

    public function rewind() {   // 第一次迭代时候会执行(或调用该方法的时候),后面的迭代将不会执行。

        echo "rewinding\n";

        reset($this->var); 

    }

 

    public function current() {

        $var = current($this->var);

        echo "current: $var\n";

        return $var;

    }

 

    public function key() {

        $var = key($this->var);

        echo "key: $var\n";

        return $var;

    }

 

    public function next() {    // 最后执行,就是执行完下面sleep(2)后再执行。(执行了next本次迭代才算结束)

        $var = next($this->var);

        echo "next: $var\n";

        return $var;

    }

 

    public function valid() {      // 当valid返回false的时候迭代结束

        $var = $this->current() !== false;

        echo "valid: {$var}\n";

        return $var;

    }

}

 

$values = array(1,2,3,4);

$it = new MyIterator($values);

 

foreach ($it as $a => $b) { // 进行迭代(每次迭代,会依次执行以下方法: rewind(特别之处见上面解释), valid, current, key, next)

    print "=====\n";

    sleep(2);

}

输出:

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

rewinding

current: 1  // 因为valid里面调用了current, 这里current出来一次

valid: 1

current: 1

key: 0

=====

next: 2

current: 2

valid: 1

current: 2

key: 1

=====

next: 3

current: 3

valid: 1

current: 3

key: 2

=====

next: 4

current: 4

valid: 1

current: 4

key: 3

=====

next:

current:

valid:    // valid返回false,迭代结束

生成器

有了yeild的方法就是一个生成器(生成器实现了Iterator接口,即一个生成器有迭代器的特点)。生成器的实现如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

function xrange($start, $end, $step = 1) {

    for ($i = $start; $i <= $end; $i += $step) {

        echo $i . "\n";

        yield;

    }

}

 

// foreach方式

foreach (xrange(1, 10) as $num) {

     

}

 

$gene = xrange(1, 10); // gene就是一个生成器对象

// current

$gene->current();  // 打印1

// next

$gene->next();

$gene->current()  // 打印2

输出:

1

2

3

4

5

6

7

8

9

10

11

12

1

2

3

4

5

6

7

8

9

10

1

2

生成器各方法详解可看文档: http://php.net/manual/zh/class.generator.php

注意:

生成器不能像函数一样直接调用,调用方法如下:

1. foreach他

2. send($value)

3. current / next...

yield

yield的语法很灵活,我们用下面的例子,让大家能明白yield语法的使用。

用例1: 让出cpu执行权

1

2

3

4

5

6

7

8

9

function task1 () {

for ($i = 1; $i <= 10; ++$i) {

        echo "This is task 1 iteration $i.\n";

        yield;// 遇到yield就会主动让出CPU的执行权;

    }

}

$a = task1();

$a->current(); // 执行第一次迭代

$a->send(1);  // 唤醒当时让出CPU执行权的yield

输出:

1

2

This is task 1 iteration 1.

This is task 1 iteration 2.

用例2: yield的返回

1

2

3

4

5

6

7

8

9

10

11

12

13

// yield返回

function task2 () {

    for ($i = 1; $i <= 10; ++$i) {

            echo "This is task 2 iteration $i.\n";

            yield "lm$i"// 遇到yield就会主动让出CPU的执行权,for暂停执行, 然后返回"lm"。放在yield后面的值就是返回值

        }

}

 

$a = task2();

$res = $a->current();  // 第一次迭代, 遇到yield返回

var_dump($res); 

$res = $a->send(1);  // 唤醒yield, for继续执行,遇到yield返回。

var_dump($res);

输出:

1

2

3

4

This is task 2 iteration 1.

string(3) "lm1"

This is task 2 iteration 2.

string(3) "lm2"

用例3: yield接收值

1

2

3

4

5

6

7

8

9

10

function task3 () {

    for ($i = 1; $i <= 10; ++$i) {

            echo "This is task 3 iteration $i.\n";

            $getValue = yield;// 遇到yield就会主动让出CPU的执行权;send后,将send值赋值给getValue

            echo $getValue . " ";

        }

}

$a = task3();

$a->current();

$a->send("aa");  // 唤醒yield,并将"aa"值赋值给$getValue变量

输出:

1

2

This is task 3 iteration 1.

aa This is task 3 iteration 2.

用例4: yeild接收和返回写在一起

1

2

3

4

5

6

7

8

9

10

11

function task4 () {

    for ($i = 1; $i <= 10; ++$i) {

        echo "This is task 4 iteration $i.\n";

        $ret = yield "lm$i"// yield, 然后返回lm$i; 当send时,将send过来的值赋值给$ret;

        echo $ret;

    }

}

$a = task4();

var_dump($a->current());     // 返回lm1

var_dump($a->send("hhh "));  // 先唤醒yield, 将"hhh "赋值给$ret,再返回lm2

var_dump($a->send("www "));  // 先唤醒yield, 将"www "赋值给$ret,再返回lm3

输出:

1

2

3

4

5

6

This is task 4 iteration 1.

string(3) "lm1"hhh

This is task 4 iteration 2.

 string(3) "lm2"www

 This is task 4 iteration 3.

 string(3) "lm3"


标签: PHP
分享:
评论:
你还没有登录,请先