这一章节“协程”话题的讨论是为了让我们对之后协程风格服务端有更全面的了解。所以我们需要先一起了解一下什么是协程?协程有什么作用?

当大家第一次看到“协程”这个词的时候,应该都一样会打开某度、某歌搜索一翻,然后搜到一堆很玄幻的概念,比如以下这一句:“协程(coroutine)也是一种程序组件。相对子例程而言,协程更为一般和灵活,但在实践中使用没有子例程那样广泛。”往往让人看了一脸问号-_-

其实“协程”这东西,我们可以这么理解,进程是个容器,对应具体干活的是线程;同样的道理,我们可以把线程看成是一个容器,协程是线程中具体干活的过程。换种说法,可以把协程想象成一个个函数(每个函数的作用不一样),而协程就是在同一个时间段内让这些函数同时工作。

协程最主要的作用是:让原来要使用异步+回调方式写的非人类代码,可以用看似同步的方式写出来(即:按串行模型去组织原本分散在不同上下文中的代码逻辑)。

网上有一些观点,说协程可以很好解决IO瓶颈问题(因为这时线程发挥不了作用)。编者只想说, IO 是系统调用,这个 IO 不是用户态能处理的,协程 是没办法绕开的,所以最终还是给堵了。如果 协程 真的能处理堵塞问题,那么很多经典的 Unix 网络编程书籍里面应该有 多协程 模式才对。

编者作为IT界的泥石流,习惯了异步的写法,对于协程抱着接受但是审视的态度。今天我们就一起看看swoole下的协编程。

我们先看看一段简易的协程代码:

echo "main start",PHP_EOL;
Co\run(function(){
echo "co 1 start",PHP_EOL; go(function(){
echo "co 2 start",PHP_EOL;echo "co 2 end",PHP_EOL;
}); go(function(){
echo "co 3 start",PHP_EOL;echo "co 3 end",PHP_EOL;
}); echo "co 1 end",PHP_EOL;
});
echo "end";

这段代码的执行结果是:

main  start

co 1 start

co 2 start

co 2 end

co 3 start

co 3 end

co 1 end

end

好像也没什么嘛,就是代码顺序执行的结果。先不要急,我们一起看看代码中比较陌生的部分:

Co\run():在Swoole直接裸写协程启动,就需要调用这个函数(其实是对 Swoole\Coroutine\Scheduler 类 (协程调度器类) 的封装),可以理解为C语言里的main()函数。(另外,Swoole 提供的 2 个进程管理模块 Process 和 Process\Pool 的 start 方法,此种启动方式会在进程启动的时候创建协程容器,参考这两个模块构造函数的 enable_coroutine 参数)。

go():添加一个子协程。

现在,我们把代码修改如下(添加了Co::sleep)

echo "main start",PHP_EOL;
Co\run(function(){
echo "co 1 start",PHP_EOL; go(function(){
echo "co 2 start",PHP_EOL;
co::sleep(1);
echo "co 2 end",PHP_EOL;
}); go(function(){
echo "co 3 start",PHP_EOL;
co::sleep(.5);
echo "co 3 end",PHP_EOL;
}); echo "co 1 end",PHP_EOL;
});
echo "end";

这时的结果会变成:

main start

co 1 start

co 2 start

co 3 start

co 1 end

co 3 end

co 2 end

end

是不是很神奇:)根据等待的时间,协程会自动调度现在可以马上就处理的代码片断。嗯,这就是协程最大的优势。

我们再做一个实验,把上面的co::sleep()函数换成php自带的sleep()函数会发生什么事?

echo "main start",PHP_EOL;
Co\run(function(){
echo "co 1 start",PHP_EOL; go(function(){
echo "co 2 start",PHP_EOL;
sleep(1); //注意这里
echo "co 2 end",PHP_EOL;
}); go(function(){
echo "co 3 start",PHP_EOL;
sleep(3); //注意这里
echo "co 3 end",PHP_EOL;
}); echo "co 1 end",PHP_EOL;
});
echo "end";

这时,我们得到的结果同样会是:

main  start

co 1 start

co 2 start

co 2 end

co 3 start

co 3 end

co 1 end

end

区别在于在co 2 start和co 3 start会被阻塞等待。究其原因就是在co::sleep()内部会用yield把时间片让出来,而sleep()则是在系统层面等待。这就是说为什么系统IO是协程绕不开的原因,该等的还是得等,只不过用了一些技巧是在其它地方等而已。

这就给了我们一些启发,如果用协程编程,碰到需要阻塞的部分(比如sleep、http请求、mysql连接、文件读写),需要用swoole为我们提供的现在协程库;如果协程库不够用,则利用异步原理与协程库实现自己的协程组件。

但是,swoole已经考虑到程序员中不乏杠精,就想把上面这请阻塞操作变成协程,只需如下操作就可以实现把代码“一键协程化”:

Swoole\Runtime::enableCoroutine();    //重点在这一句
echo "main start",PHP_EOL;
Co\run(function(){
echo "co 1 start",PHP_EOL; go(function(){
echo "co 2 start",PHP_EOL;
sleep(2);
echo "co 2 end",PHP_EOL;
}); go(function(){
echo "co 3 start",PHP_EOL;
sleep(1);
echo "co 3 end",PHP_EOL;
}); echo "co 1 end",PHP_EOL;
});
echo "end";

虽然swoole提供了“一键协程化”的神仙操作,可以把文件操作,sleep,Mysqli,PDO,streams等都变成异步IO,但是要注意,底层是使用了HOOK的方式把原PHP的代码调用转移到了swoole的函数内,可参考这里。所以认识swoole提供的原生协程库也是非常重要的。

要注意的是:Swoole\Runtime::enableCoroutine(),并不针对curl。如果想要把curl也协程化,需要调用Swoole\Runtime::enableCoroutine($flags = SWOOLE_HOOK_ALL | SWOOLE_HOOK_CURL); 对于Co::set(['hook_flags' => SWOOLE_HOOK_ALL | SWOOLE_HOOK_CURL]); 也是一样。

接下来,再来一段代码:

echo "main start",PHP_EOL;
echo "co 1 start",PHP_EOL; go(function(){
echo "co 2 start",PHP_EOL;
co::sleep(1);
echo "co 2 end",PHP_EOL;
}); go(function(){
echo "co 3 start",PHP_EOL;
co::sleep(.5);
echo "co 3 end",PHP_EOL;
}); echo "co 1 end",PHP_EOL; echo "end",PHP_EOL;

我们得到的结果是:

main start

co 1 start

co 2 start

co 3 start

co 1 end

end

co 2 end

co 2 end

在这一段代码中,没有协程容器Co\run()的存在,go()子协程同样会被调用。可以看出协程容器可以保证容器内的协程代码全部执行完成后,再跳出容器往下执行外部的代码。

好了,协程初探就先到这里了。接下去,我们会先了解协程版本的TCP服务器。关于swoole协程的其它话题,我们后续会进行讨论。

---------------------------  我是可爱的分割线  ----------------------------

最后博主借地宣传一下,漳州编程小组招新了,这是一个面向漳州青少年信息学/软件设计的学习小组,有意向的同学点击链接,联系我吧。

Swoole从入门到入土(8)——协程初探的更多相关文章

  1. 数据结构和算法(Golang实现)(6)简单入门Golang-并发、协程和信道

    并发.协程和信道 Golang语言提供了go关键字,以及名为chan的数据类型,以及一些标准库的并发锁等,我们将会简单介绍一下并发的一些概念,然后学习这些Golang特征知识. 一.并发介绍 我们写程 ...

  2. Generator(生成器),入门初基,Coroutine(原生协程),登峰造极,Python3.10并发异步编程async底层实现

    普遍意义上讲,生成器是一种特殊的迭代器,它可以在执行过程中暂停并在恢复执行时保留它的状态.而协程,则可以让一个函数在执行过程中暂停并在恢复执行时保留它的状态,在Python3.10中,原生协程的实现手 ...

  3. python从入门到放弃之协程

    协程 协程,又称微线程,纤程.英文名Coroutine. 协程的概念很早就提出来了,但直到最近几年才在某些语言(如Lua)中得到广泛应用. 子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B ...

  4. [Swoole系列入门教程 5] UDP协议和demo

    • 客户端服务端没有任何联系 • 指定地址跟端口,不关心消息是否发送成功 • 心跳检测不能影响到客户端• udp建立长连接

  5. [Sw] Swoole-4.2.9 可以尝试愉快应用 Swoole 协程

    大家知道 Swoole 提供了方便于服务器.网络编程的模式,简化了多进程编程. 这直接让 PHP 的运行很容易变成常驻内存的 Server 程序,执行效率上有了数倍的提升. 但是这一切还没有让人足够兴 ...

  6. Golang 入门 : goroutine(协程)

    在操作系统中,执行体是个抽象的概念.与之对应的实体有进程.线程以及协程(coroutine).协程也叫轻量级的线程,与传统的进程和线程相比,协程的最大特点是 "轻"!可以轻松创建上 ...

  7. swoole使用协程

    协程:协程可以理解为纯用户态的线程,其通过协作而不是抢占来进行切换.相对于进程或者线程,协程所有的操作都可以在用户态完成,创建和切换的消耗更低.Swoole可以为每一个请求创建对应的协程,根据IO的状 ...

  8. 协程与Swoole的原理,相关应用以及适用场景等

    什么是协程 协程(Coroutine)也叫用户态线程,其通过协作而不是抢占来进行切换.相对于进程或者线程,协程所有的操作都可以在用户态完成,创建和切换的消耗更低.协程是进程的补充,或者是互补关系. 要 ...

  9. [Swoole入门到进阶] [公开课] Swoole协程-Swoole4.4.4 提供 WaitGroup 功能

    在 Swoole4 中可以使用 channel 实现协程间的通信.依赖管理.协程同步. 简单来说,WaitGroup 就是使用 channel 的机制,让主协程等待所有子协程结束后才退出的功能. Co ...

  10. swoole| swoole 协程初体验 转

    swoole| swoole 协程初体验   date: 2018-5-30 14:31:38title: swoole| swoole 协程初体验description: 通过协程的执行初窥 swo ...

随机推荐

  1. 百度网盘(百度云)SVIP超级会员共享账号每日更新(2024.01.21)

    一.百度网盘SVIP超级会员共享账号 可能很多人不懂这个共享账号是什么意思,小编在这里给大家做一下解答. 我们多知道百度网盘很大的用处就是类似U盘,不同的人把文件上传到百度网盘,别人可以直接下载,避免 ...

  2. [转帖]修改Linux内核参数,减少TCP连接中的TIME-WAIT

    https://www.cnblogs.com/xiaoleiel/p/8340346.html 一台服务器CPU和内存资源额定有限的情况下,如何提高服务器的性能是作为系统运维的重要工作.要提高Lin ...

  3. [转帖]Nginx中absolute_redirect、port_in_redirect配置简述

    一.背景: 我们在访问nginx代理下的项目前端页面时,经常会有访问链接最末尾带不带'/'的问题,当我们访问http://xxxx/home时,如果匹配不到location,会自动加上端口port以及 ...

  4. [转帖]ESX/ESXi 主机上的每个插槽中安装了多少内存

    https://www.cnblogs.com/reachos/p/11242302.html 要确定在 ESX/ESXi 主机上的每个插槽中安装了多少内存,请执行以下操作: 1. 启动ssh服务 2 ...

  5. [转帖]--build=arm-linux

    今天在arm上用configure生成makefile时报错:configure: error: cannot guess build type; you must specify one 问题: 不 ...

  6. 【转帖】Linux性能优化(十六)——中断绑定

    一.中断绑定简介 1.中断简介 计算机中,中断是一种电信号,由硬件产生并直接送到中断控制器上,再由中断控制器向CPU发送中断信号,CPU检测到信号后,中断当前工作转而处理中断信号.CPU会通知操作系统 ...

  7. CentOS8 解决 yum元数据下载失败的方法

    背景 最近同事反馈CentOS的机器无法再现安装 rpm包了. 提示信息主要是下载源数据失败. 今天上午抽时间进行了一下简单验证, 晚上进行一下总结. 避免遗忘. 认为与Redhat被IBM收购之后, ...

  8. React类组件中事件绑定this指向的三种方式

    有状态组件和无状态组件 函数组件又叫做无状态组件,类组件又叫做有状态组件. 状态又叫做数据 函数组件没有自己的状态,只负责静态页面的展示. 我们可以理解为纯ui展示.() 类组件有自己的状态,扶着更新 ...

  9. 解决Chrome翻译无法使用

    截止2022年11月3日自己ping出的ip不可用了 可以用以下ip 172.217.215.90 172.253.115.90 142.250.126.90 142.250.10.90 142.25 ...

  10. 手撕Vue-数据驱动界面改变中

    经过上一篇的介绍,已经实现了观察者模式的基本内容,接下来要完成的就是将上一篇的发布订阅模式运用到 Nue 中,实现数据驱动界面改变. 在监听数据变化的章节当中,根据指定的区域和数据去编译渲染界面 这个 ...