专栏首页Web技术布道师PHP 协程:Go + Chan + Defer

PHP 协程:Go + Chan + Defer

Swoole4提供了强大的PHP CSP协程编程模式。底层提供了3个关键词,可以方便地实现各类功能。

关键词

  • go :创建一个协程
  • chan :创建一个通道
  • defer :延迟任务,在协程退出时执行,先进后出

3个功能底层实现全部为内存操作,没有任何IO资源消耗。就像PHPArray一样是非常廉价的。如果有需要就可以直接使用。这与socketfile操作不同,后者需要向操作系统申请端口和文件描述符,读写可能会产生阻塞的IO等待。

协程并发

使用go函数可以让一个函数并发地去执行。在编程过程中,如果某一段逻辑可以并发执行,就可以将它放置到go协程中执行。

顺序执行

function test1()
{
   sleep(1);
   echo "b";
}
   
function test2()
{
   sleep(2);
   echo "c";
}test1();
test2();

执行结果:

htf@LAPTOP-0K15EFQI:~$ time php b1.php
bc
real    0m3.080s
user    0m0.016s
sys     0m0.063s
htf@LAPTOP-0K15EFQI:~$

上述代码中,test1test2会顺序执行,需要3秒才能执行完成。

并发执行

使用go创建协程,可以让test1test2两个函数变成并发执行。

Swoole\Runtime::enableCoroutine();go(function ()
{
   sleep(1);
   echo "b";
});
   
go(function ()
{
   sleep(2);
   echo "c";
});

执行结果:

bchtf@LAPTOP-0K15EFQI:~$ time php co.php
bc
real    0m2.076s
user    0m0.000s
sys     0m0.078s
htf@LAPTOP-0K15EFQI:~$

可以看到这里只用了2秒就执行完成了。

  • 顺序执行耗时等于所有任务执行耗时的总和 :t1+t2+t3...)
  • 并发执行耗时等于所有任务执行耗时的最大值 :max(t1, t2, t3, ...)

协程通信

有了go关键词之后,并发编程就简单多了。与此同时又带来了新问题,如果有2个协程并发执行,另外一个协程,需要依赖这两个协程的执行结果,如果解决此问题呢?

答案就是使用通道(Channel),在Swoole4协程中使用new chan就可以创建一个通道。通道可以理解为自带协程调度的队列。它有两个接口pushpop

  • push:向通道中写入内容,如果已满,它会进入等待状态,有空间时自动恢复
  • pop:从通道中读取内容,如果为空,它会进入等待状态,有数据时自动恢复

使用通道可以很方便地实现并发管理。

$chan = new chan(2);# 协程1
go (function () use ($chan) {
   $result = [];
   for ($i = 0; $i < 2; $i++)
   {
       $result += $chan->pop();
   }
   var_dump($result);
});# 协程2
go(function () use ($chan) {
  $cli = new Swoole\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]);
});# 协程3
go(function () use ($chan) {
  $cli = new Swoole\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 响应内容过大,这里用 Http 状态码作为测试
  $chan->push(['www.163.com' => $cli->statusCode]);
});

执行结果:

htf@LAPTOP-0K15EFQI:~/swoole-src/examples/5.0$ time php co2.php
array(2) {
 ["www.qq.com"]=>
 int(302)
 ["www.163.com"]=>
 int(200)
}real    0m0.268s
user    0m0.016s
sys     0m0.109s
htf@LAPTOP-0K15EFQI:~/swoole-src/examples/5.0$

这里使用go创建了3个协程,协程2和协程3分别请求qq.com163.com主页。协程1需要拿到Http请求的结果。这里使用了chan来实现并发管理。

  • 协程1循环两次对通道进行pop,因为队列为空,它会进入等待状态
  • 协程2和协程3执行完成后,会push数据,协程1拿到了结果,继续向下执行

延迟任务

在协程编程中,可能需要在协程退出时自动实行一些任务,做清理工作。类似于PHPregister_shutdown_function,在Swoole4中可以使用defer实现。

Swoole\Runtime::enableCoroutine();go(function () {
   echo "a";
   defer(function () {
       echo "~a";
   });
   echo "b";
   defer(function () {
       echo "~b";
   });
   sleep(1);
   echo "c";
});

执行结果:

htf@LAPTOP-0K15EFQI:~/swoole-src/examples/5.0$ time php defer.php
abc~b~a
real    0m1.068s
user    0m0.016s
sys     0m0.047s
htf@LAPTOP-0K15EFQI:~/swoole-src/examples/5.0$

结语

Swoole4提供的Go + Chan + DeferPHP带来了一种全新的CSP并发编程模式。灵活使用Swoole4提供的各项特性,可以解决工作中各类复杂功能的设计和开发。

本文分享自微信公众号 - PHP技术大全(phpgod),作者:韩天峰

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-01-16

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • MySQL -- 短连接 + 慢查询

    KILL CONNECTION:主动踢除不需要保持的连接(与wait_timeout的效果一样)

    猿哥
  • SMProxy:基于 MySQL 协议,Swoole 开发的 MySQL 数据库连接池

    将数据库连接作为对象存储在内存中,当用户需要访问数据库时,首次会建立连接,后面并非建立一个新的连接,而是从连接池中取出一个已建立的空闲连接对象。 使用完毕后,用...

    猿哥
  • PHP FFI 允许在 PHP 脚本中嵌入原始 C 代码

    Zend 的 Dmitry Stogov 通过允许 PHP 执行嵌入式 C 代码扩展了 PHP 的领域。 这将允许完全访问本地 C 函数,变量以及数据结构。

    猿哥
  • Flutter | WReorderList 一个可以指定两个item互换位置的组件

    最近遇到个需求,要求在一个 ListView 里面能互换两个 item 的位置,这样:

    Flutter笔记
  • 【RL-TCPnet网络教程】第26章 RL-TCPnet之DHCP应用

    本章节为大家讲解RL-TCPnet的DHCP应用,学习本章节前,务必要优先学习第25章的DHCP基础知识。有了这些基础知识之后,再搞本章节会有事半功倍的效果。

    armfly
  • testlib.h从入门到入坟

    attack
  • TCP/IP(一)

    IP协议负责把数据从一台计算机通过网络发送到另一台计算机。数据被分割成一小块一小块,然后通过IP包发送出去。由于互联网链路复杂,两台计算机之间经常有多条线路,因...

    一点儿也不潇洒
  • struct sqlite3

    {   sqlite3_vfs *pVfs;            /* OS Interface */   struct Vdbe *pVdbe;      ...

    py3study
  • 线程同步(四)—— 信号

    Aichen
  • kotlin 协程官方文档(1)-协程基础(Coroutine Basics)

    本质上,协程可以称为轻量级线程。协程在 CoroutineScope (协程作用域)的上下文中通过 launch、async 等协程构造器(coroutine ...

    叶应是叶

扫码关注云+社区

领取腾讯云代金券