Swoole4为PHP语言提供了强大的CSP协程编程模式。底层提供了3个关键字,可以方便的实现各种功能。Swoole4提供的PHP协程语法是借鉴了Golang。在此向GO开发团队致敬。PHP+Swoole协程可以很好的补充Golang。Golang:静态语言,严谨,强大,性能好,PHP+Swoole:动态语言,灵活易用延迟任务在协程退出时执行。先进后出这三个功能的底层实现都是内存操作,没有任何IO资源消耗。就像PHP的Array很便宜一样。有需要的可以直接使用。这不同于socket和文件操作,需要向操作系统申请端口和文件描述符,读写可能会产生阻塞IO等待。协程并发使用go函数来允许一个函数并发执行。在编程过程中,如果某段逻辑可以并发执行,可以放到一个go协程中执行。执行函数test1(){睡眠(1);echo"b";}functiontest2(){sleep(2);echo"c";}test1();test2();执行结果:htf@LAPTOP-0K15EFQI:~$timephpb1.phpbcreal0m3.080suser0m0.016ssys0m0.063shtf@LAPTOP-0K15EFQI:~$上面代码中,test1和test2会依次执行,耗时3秒来完成执行。并发执行使用go创建一个协程,可以让test1和test2这两个函数成为并发执行。Swoole\Runtime::enableCoroutine();go(function(){sleep(1);echo"b";});go(function(){sleep(2);echo"c";});Swoole\Runtime::enableCoroutine()的作用是切换PHP提供的stream、sleep、pdo、mysqli、redis等函数从同步阻塞到协程异步IO执行结果:bchtf@LAPTOP-0K15EFQI:~$timephpco.phpbcreal0m2。076suser0m0.000ssys0m0.078shtf@LAPTOP-0K15EFQI:~$可以看出执行完成只用了2秒。顺序执行时间等于所有任务执行时间的总和:t1+t2+t3...并发执行时间等于所有任务执行时间的最大值:max(t1,t2,t3,...)coroutine通信有了go关键字,并发编程就简单多了。同时,也带来了新的问题。如果有两个协程并发执行,另一个协程需要依赖这两个协程的执行结果。如何解决这个问题呢?答案是使用通道(Channel),在Swoole4协程中使用newchan创建通道。通道可以理解为一个队列,有自己的协程调度。它有push和pop两个接口:push:往channel中写入内容,如果满了,进入等待状态,有空间时自动恢复pop:从channel中读取内容,如果为空,则自动恢复进入waitingstate状态,有数据时自动恢复使用channels可以轻松实现并发管理。$chan=newchan(2);#Coroutine1go(function()use($chan){$result=[];for($i=0;$i<2;$i++){$result+=$chan->pop();}var_dump($result);});#Coroutine2go(function()use($chan){$cli=newSwoole\Coroutine\Http\Client('www.qq.com',80);$cli->set(['timeout'=>10]);$cli->setHeaders(['Host'=>"www.qq.com","User-Agent"=>'Chrome/49.0.2587.3','Accept'=>'text/html,application/xhtml+xml,application/xml','Accept-Encoding'=>'gzip',]);$ret=$cli->get('/');//$cli->body响应内容太大,这里使用Http状态码作为测试$chan->push(['www.qq.com'=>$cli->statusCode]);});#Coroutine3go(function()use($chan){$cli=newSwoole\Coroutine\Http\Client('www.163.com',80);$cli->set(['timeout'=>10]);$cli->setHeaders(['Host'=>"www.163.com","User-Agent"=>'Chrome/49.0.2587.3','Accept'=>'text/html,application/xhtml+xml,application/xml','Accept-Encoding'=>'gzip',]);$ret=$cli->get('/');//$cli->body响应内容太大,这里使用HttpstatusCode作为测试$chan->push(['www.163.com'=>$cli->statusCode]);});执行结果:htf@LAPTOP-0K15EFQI:~/swoole-src/examples/5.0$timephpco2.phparray(2){["www.qq.com"]=>int(302)["www.163.com"]=>int(200)}real0m0.268suser0m0.016ssys0m0.109shtf@LAPTOP-0K15EFQI:~/swoole-src/examples/5.0$这里使用go创建了3个协程。协程2和协程3分别请求qq.com和163.com主页。协程1需要获取Http请求结果。这里使用Chan来实现并发管理。协程1循环两次以弹出通道。因为队列是空的,所以会进入等待状态。协程2和协程3执行完后,会推送数据。协程1得到结果,继续向下执行延迟的任务。在协程编程中,可能需要在协程退出时自动执行一些任务并进行清理。类似于PHP的register_shutdown_function,在Swoole4中可以使用defer来实现。Swoole\Runtime::enableCoroutine();go(function(){echo"a";defer(function(){echo"~a";});echo"b";defer(function(){echo"~b";});睡眠(1);回声"c";});执行结果:htf@LAPTOP-0K15EFQI:~/swoole-src/examples/5.0$timephpdefer.phpabc~b~areal0m1.068suser0m0.016ssys0m0.047shtf@LAPTOP-0K15EFQI:~/swoole-src/examples/5.0$结语Swoole4提供的Go+Chan+Defer为PHP带来了全新的CSP并发编程模式。灵活运用Swoole4提供的各种特性,可以解决工作中各种复杂功能的设计和开发。
