分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > 技术分享

PHP协程入门详解

发布时间:2023-09-06 02:06责任编辑:董明明关键词:PHP

 概念

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

  1. 协同。因为是由程序员自己写的调度策略,其通过协作而不是抢占来进行切换
  2. 在用户态完成创建,切换和销毁

PHP对协程的支持是在迭代生成器的基础上, 增加了可以回送数据给生成器的功能(调用者发送数据给被调用的生成器函数)。 这就把生成器到调用者的单向通信转变为两者之间的双向通信。

迭代器

迭代器的概念这里就不赘述了。下面看看我们自己实现的一个迭代器。

 1 class MyIterator implements Iterator 2 { 3 ????private $var = array(); 4 ?5 ????public function __construct($array) 6 ????{ 7 ????????if (is_array($array)) { 8 ????????????$this->var = $array; 9 ????????}10 ????}11 12 ????public function rewind() { ??// 第一次迭代时候会执行(或调用该方法的时候),后面的迭代将不会执行。13 ????????echo "rewinding\n";14 ????????reset($this->var); ?15 ????}16 17 ????public function current() {18 ????????$var = current($this->var);19 ????????echo "current: $var\n";20 ????????return $var;21 ????}22 23 ????public function key() {24 ????????$var = key($this->var);25 ????????echo "key: $var\n";26 ????????return $var;27 ????}28 29 ????public function next() { ???// 最后执行,就是执行完下面sleep(2)后再执行。(执行了next本次迭代才算结束)30 ????????$var = next($this->var);31 ????????echo "next: $var\n";32 ????????return $var;33 ????}34 35 ????public function valid() { ?????// 当valid返回false的时候迭代结束36 ????????$var = $this->current() !== false;37 ????????echo "valid: {$var}\n";38 ????????return $var;39 ????}40 }41 42 $values = array(1,2,3,4);43 $it = new MyIterator($values);44 45 foreach ($it as $a => $b) { // 进行迭代(每次迭代,会依次执行以下方法: rewind(特别之处见上面解释), valid, current, key, next)46 ????print "=====\n";47 ????sleep(2);48 }

输出:

rewindingcurrent: 1 ?// 因为valid里面调用了current, 这里current出来一次valid: 1current: 1key: 0=====next: 2current: 2valid: 1current: 2key: 1=====next: 3current: 3valid: 1current: 3key: 2=====next: 4current: 4valid: 1current: 4key: 3=====next: current: valid: ???// valid返回false,迭代结束

生成器

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

 1 function xrange($start, $end, $step = 1) { 2 ????for ($i = $start; $i <= $end; $i += $step) { 3 ????????echo $i . "\n"; 4 ????????yield; 5 ????} 6 } 7 ?8 // foreach方式 9 foreach (xrange(1, 10) as $num) {10 ????11 }12 13 $gene = xrange(1, 10); // gene就是一个生成器对象14 // current15 $gene->current(); ?// 打印116 // next17 $gene->next();18 $gene->current() ?// 打印2

输出:

1234567891012

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

注意:

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

1. foreach他

2. send($value)  

3. current / next...

yield

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

用例1: 让出cpu执行权

 1 function task1 () { 2 for ($i = 1; $i <= 10; ++$i) { 3 ????????echo "This is task 1 iteration $i.\n"; 4 ????????yield;// 遇到yield就会主动让出CPU的执行权; 5 ????} 6 } 7 ?8 $a = task1(); ?9 $a->current(); // 执行第一次迭代10 $a->send(1); ?// 唤醒当时让出CPU执行权的yield

输出:

This is task 1 iteration 1.This is task 1 iteration 2.

用例2: yield的返回

 1 // yield返回 2 function task2 () { 3 ????for ($i = 1; $i <= 10; ++$i) { 4 ????????????echo "This is task 2 iteration $i.\n"; 5 ????????????yield "lm$i"; ?// 遇到yield就会主动让出CPU的执行权,for暂停执行, 然后返回"lm"。放在yield后面的值就是返回值 6 ????????} 7 } 8 ?9 $a = task2(); 10 $res = $a->current(); ?// 第一次迭代, 遇到yield返回11 var_dump($res); ?12 $res = $a->send(1); ?// 唤醒yield, for继续执行,遇到yield返回。13 var_dump($res); 

输出:

This is task 2 iteration 1.string(3) "lm1"This is task 2 iteration 2.string(3) "lm2"

用例3: yield接收值

 1 function task3 () { 2 ????for ($i = 1; $i <= 10; ++$i) { 3 ????????????echo "This is task 3 iteration $i.\n"; 4 ????????????$getValue = yield;// 遇到yield就会主动让出CPU的执行权;send后,将send值赋值给getValue 5 ????????????echo $getValue . " "; 6 ????????} 7 } 8 ?9 $a = task3(); 10 $a->current();11 $a->send("aa"); ?// 唤醒yield,并将"aa"值赋值给$getValue变量

输出:

This is task 3 iteration 1.aa This is task 3 iteration 2. ?

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

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()); ????// 返回lm1var_dump($a->send("hhh ")); ?// 先唤醒yield, 将"hhh "赋值给$ret,再返回lm2var_dump($a->send("www ")); ?// 先唤醒yield, 将"www "赋值给$ret,再返回lm3

输出:

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"

结语:

如果你有看过鸟哥的这篇文章http://www.laruence.com/2015/05/28/3038.html,应该对协程有个深刻的认识。但里面内容更适合中高级PHP工程师看,而且还得具备一定的操作系统的知识,所以我在此基础上用更通俗的方式,阐明一下PHP的协程概念。协程很强大的功能但相对比较复杂, 也比较难被理解。个人目前还没有遇到合适的场景来使用PHP协程,不过我猜测,由于可以在用户层面实现多并发,所以多用于CLI模式下的web服务开发,比如Golang的goroutine并不是线程,而是协程。还有yield有双向通信的功能,所以还可以实现异步服务,但需要自己写调度器,比如鸟哥这篇博客里面的非阻塞IOweb服务器就是靠协程实现异步了实现的。 

以上内容如果有错误还请留言交流。

PHP协程入门详解

原文地址:https://www.cnblogs.com/xiaoxlm/p/9392249.html

知识推荐

我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved