大家好,我是老李。

顺风说骚话,逆风讲道理

最近在大家一起努力下,那个沙雕肺炎患病人数增长率下降了不少,总体来说还算顺,所以今天这篇注定又要骚话连篇了。听说最近不少玉米开始向大连、威海、烟台方向涌入,算是潜在的风险吧,总之,大家也都别放松警惕,不要看到曙光时候给撂倒了。

上一节课我们说的主要是在谢顶道人 --- 老李的提示下,你初步使用了yield...那个你没有名字也不好,给你起个名字,洋气点儿就叫欧阳吧,一听就是个贵族富少继承者们。

喊人先喊亲,要把读者叫舒心

文章别嫌少,得把客官伺候好

开工第一天,你的老板原上草又交给你一个任务并信誓旦旦地答应你如果你能顺利完成任务那么这个月的打卡迟到费就不扣你的了。强大的利好信息让你欲罢不能。事情是这样的,还是上一集那个内存只有100KB的单片机,这个单片机会访问三方服务公司的一个API,但有时候这个API会抽风,老板原上草的意思是这会儿不要阻塞等待而是让这个单片机干点儿别的事儿,等API访问OK了再让TA回来,总之别让TA闲着,算是免费送这个单片机一个满满的福报。

考虑到昨天谢顶道人曾经给你科普过的yield似乎拥有一种让出CPU实现用户调度的能力,你决定展现一波儿自我,而谢顶道人也决定用那双充满了老茧子的手手把手辅导你。

<?php function gen1() {  for( $i = 1; $i <= 10; $i++ ) {    echo "GEN1 : {$i}".PHP_EOL;    // sleep没啥意思,主要就是运行时候给你一种切实的调度感,你懂么    sleep( 1 );    // 这句很关键,表示自己主动让出CPU,我不下地狱谁下地狱    yield;  }}function gen2() {  for( $i = 1; $i <= 10; $i++ ) {    echo "GEN2 : {$i}".PHP_EOL;    // sleep没啥意思,主要就是运行时候给你一种切实的调度感,你懂么    sleep( 1 );    // 这句很关键,表示自己主动让出CPU,我不下地狱谁下地狱    yield;  }}$task1 = gen1();$task2 = gen2();while( true ) {  // 首先我运行task1,然后task1主动下了地狱  echo $task1->current();  // 这会儿我可以让task2介入进来了  echo $task2->current();  // task1恢复中断  $task1->next();  // task2恢复中断  $task2->next();}

GET不到发生了什么,是吗?就是gen1()和gen2()可以交替运行并且每次都是接着从上次的地方开始运行,你要用传统的function是完全做不到的,传统的function只能一口气先完成其中一个函数中的for()然后再能完成另外一个function中的for(),比如下面这坨:

<?php function gen1() {  for( $i = 1; $i <= 10; $i++ ) {    echo "GEN1 : {$i}".PHP_EOL;    sleep( 1 );  }}function gen2() {  for( $i = 1; $i <= 10; $i++ ) {    echo "GEN2 : {$i}".PHP_EOL;  }}gen1();gen2();// 看这里,看这里,看这里!// 上面的代码一旦运行,一定是先运行完gen1函数中的for循环// 其次才能运行完gen2函数中的for循环,绝对不会出现// gen1和gen2交叉运行这种情况

我似乎已然精通了yield

好了欧阳,让我们展示真正的技术吧!下面这个demo,如果访问某个API阻塞的话就主动让出CPU,然后让出的CPU开始往一个文件里写字符串...反正不能让TA闲着:

<?php $ch1 = curl_init();// 这个地址中的php,我故意sleep了5秒钟,然后输出一坨jsoncurl_setopt( $ch1, CURLOPT_URL, "http://www.selfctrler.com/index.php/test/test1" );curl_setopt( $ch1, CURLOPT_HEADER, 0 );$mh = curl_multi_init();curl_multi_add_handle( $mh, $ch1 );// gen1中就是调用三方API,基于multi-curl实现function gen1( $mh, $ch1 ) {  do {    $mrc = curl_multi_exec( $mh, $running );    // 请求发出后,让出cpu    $rs = yield;    // 生产环境千万别这么干......    // 这里加sleep是为了让你看的更清楚流程    sleep( 1 );    echo "收到外部发送数据{$rs}".PHP_EOL;      } while( $running > 0 );  $ret = curl_multi_getcontent( $ch1 );  echo $ret.PHP_EOL;  return false;}// gen2是写文件...function gen2() {  for ( $i = 1; $i <= 10; $i++ ) {    echo "gen2 : {$i}".PHP_EOL;    file_put_contents( "./yield.log", "gen2".$i.PHP_EOL, FILE_APPEND );    $rs = yield;    // 生产环境千万别这么干......    // 这里加sleep是为了让你看的更清楚流程    sleep( 1 );    echo "收到外部发送数据{$rs}".PHP_EOL;      }}$gen1 = gen1( $mh, $ch1 );$gen2 = gen2();while( true ) {  echo $gen1->current();  echo $gen2->current();  $gen1->send("gen1");  $gen2->send("gen2");}

上面这坨代码在飞起来后,我们再等待curl发起请求的5秒钟内,同时可以完成文件写入功能,如果换做平时的PHP程序,就只能是先阻塞等待curl拿到结果后才能完成文件写入,有了一丝丝内味儿了吗?

协程味儿

但是这里必须要值得注意的是,欧阳在gen1()的代码里用的并不是我们一般时候用的curl方法,而是curl_multi_exec(),为啥呢?因为一般般我们最常用的PHP curl方法都是阻塞的,这很致命,这里要点就是:全程不能阻塞,阻塞一处死翘翘。实际上这里最标准的用法就是curl_multi_exec()配合curl_multi_select()。所以,扩散一下思维如果你用file_get_contents()也是不行的。

下面由谢顶道人总结一个PHP中yield的典型使用方法:如果要使用yield实现「异步」,实际上在PHP里也只能是结合select或epoll这些IO服用,具体就是当IO没有ready的时候,yield出让CPU去做别的事情,一旦IO ready了就回来继续执行原来的任务,说白了就是协程调度器!

???

那TM我要这yield到底有啥用?谢顶道人你咋这幽默呢?感觉蒙娜丽莎都是你逗笑的呢~我直接用之前章节里基于libevent实现的服务器不就挺好用的吗?这里要说的就是「基于IO复用实现的异步非阻塞服务器中难以避免的异步回调地狱」写法,说白了就是一层又一层嵌套的on。这在NodeJS里颇为常见,所以后来NodeJS出了一个叫做Promise的关键字来缓解这个问题,这里你可以粗暴的认为yield就是PHP版本的Promise,就是传说中的「用传统同步代码的写法写异步」,但也依然能写出高IO的程序。

世面上有什么典型作品吗?有啊,swoole呀,swoole协程就是基于epoll实现的协程调度器;还有微信开源的libco也基本上是基于IO复用实现的协程调度器。要注意的基于epoll实现协程调度器只是一种实现方式而已,像Golang则是完全是自己在上层实现的调度器。

好看的皮囊就是好看,有趣的灵魂爱咋咋滴...

php curl header_PHP中的yield与协程(二十一节)相关推荐

  1. 填坑之PHP的yield和协程在一起的日子里

    首先是,这是我第一次把公众号文章复制粘贴到github来. 其次是,很久很久之前,我挖了一个yield的一个坑,自己挖的坑自己填,不然迟早会把自己埋掉. 最后是,如果想看之前那个坑,请发送" ...

  2. c++ 协程_用yield实现协程

    上一篇 理解python中的yield关键字 介绍了使用yeld实现生成器函数,这一篇我们来继续深入的了解一下yield,用yield实现协程. 先来解答一下上一篇留下的问题:下面的代码为什么第二次调 ...

  3. Python_oldboy_自动化运维之路_线程,进程,协程(十一)

    本节内容: 线程 进程 协程 IO多路复用 自定义异步非阻塞的框架 线程和进程的介绍: 举个例子,拿甄嬛传举列线程和进程的关系: 总结: 1.工作最小单元是线程,进程说白了就是提供资源的 2.一个应用 ...

  4. C++协程(二):Understanding operator co_await

    本文翻译自c++协程库cppcoro库作者Lewis Baker的github post,本篇为第二篇,原文内容在https://lewissbaker.github.io/2017/09/25/co ...

  5. Python中多线程多进程与协程的区别

    进程:一个运行的程序(代码)就是一个进程,没有运行的代码叫程序,进程是系统资源分配的最小单位,进程拥有自己独立的内存空间,所以进程间数据不共享,开销大. 线程: 调度执行的最小单位,也叫执行路径,不能 ...

  6. 简要说明__python3中的进程/线程/协程

    多任务可以充分利用系统资源,极大提升程序运行效率,多任务的实现往往与 多线程,多进程,多协程有关 稳定性: 进程 > 线程 > 协程 系统资源占用量:进程 > 线程 > 协程 ...

  7. python中IO多路复用、协程

    一.IO多路复用 IO多路复用:检测多个socket是否已经发生变化(是否已经连接成功/是否已经获取数据)(可读/可写) import socket def get_data(key):client ...

  8. Go语言中开启和退出协程

    一 协程的概念 协程(coroutine)是Go语言中的轻量级线程实现:与线程不同的是,协程不受操作系统调度,协程的调度由用户程序自行提供:Go语言中的协程调度器将协程调度到线程中运行 二 协程的开启 ...

  9. 易宝典文章——玩转Office 365中的Exchange Online服务 之二十一 怎样通过中继收件人实现邮件审核...

    根据企业的行政制度或保密策略要求,可能需要控制发送到某些邮箱或大型通讯组的邮件.比如:  >越级向高级别的高管发送邮件:  >向合作伙伴或大客户联系人发送邮件:  >向公司全体员工通 ...

最新文章

  1. elasticsearch的集中常见操作
  2. form action可以变量么_在 Laravel 中优雅处理 Form 表单
  3. 那些年,我在西安的“遇见”(一)
  4. 【SpringCloud】Spring Cloud bus
  5. python入门代码-python基础知识和练习代码
  6. matlab中三种原油问题,基于MATLAB联合站原油加热模糊控制(程序)
  7. 103000大写加零吗_00支票大写金额该如何?
  8. MOS管导通条件概述-过程-压降-提高效率等详解
  9. 互联网创业怎么做?分享我的7个网创实操心得
  10. JavaScript实现输入框(密码框)出现提示语
  11. 八爪鱼采集ajax表格,设置八爪鱼采集器ajax延时采集分页列表的方法
  12. 法定节假日加班没给三薪是不是犯法的
  13. 华三防火墙添加web用户_H3C防火墙系列三:WEB管理
  14. 平凡的世界,因你而美
  15. Julia数据可视化:Gadfly.jl包的使用
  16. 高通平台Linux kernel死机解题心得-trace32的使用
  17. python namedtuple默认值_python 使用 namedtuple
  18. 如何用利特尔法则调整线程池大小
  19. AI赋能安全 腾讯云发布三大安全新品与三大行业安全解决方案
  20. 分析DLL搜索顺序劫持的原理

热门文章

  1. 计算机改成服务器,旧电脑主机如何改成服务器
  2. qt android 应用程序图标大小,vs+qt 设置应用程序图标
  3. 公众号 接收规则 消息_微信公众平台 发送模板消息(Java接口开发)
  4. js整体缩小网页_SEO网页优化的原则是什么?
  5. php url乱码java接收,java中url乱码解决方法
  6. c语言课程设计模块结构图,【图片】发几个C语言课程设计源代码(恭喜自己当上技术小吧主)【东华理工大学吧】_百度贴吧...
  7. mysql查询数据为0的所有字段6_MySQL8.0 初级学习笔记(六)查询数据
  8. MATLAB自定义画布大小
  9. MATLAB获得子图位置
  10. 数字图像处理:四连通域与八连通域