协程说复杂不复杂说难也不难,一句话能够归纳:能提升并发,但不能加速任务,同步代码实现异步IO,异步非阻塞的代码块。php
协程是一种特殊函数,是一种能够挂起的函数,而后能够从挂起的地方从新恢复执行,一个线程内的多个协程是串行的,跟CPU处理进程同样,同一时刻只能一个协程在线程上运行,除非出让了控制权给别的协程运行。协程没法利用多核CPU所以协程只能解决并发问题,不能解决任务处理速度问题。协程就是把一个大任务再分红更小的片断,封装程一个函数,当其中一个协程须要IO阻塞的时候,主动挂起当前协程,把控制权交给其余协程运行。laravel
咱们知道进程和线程是由操做系统调度的,何时执行取决于操做系统何时把CPU时间交给某个进程或者线程,而协程是何时交出控制权是由用户决定的。进程和线程属于内核态,协程属于用户态线程。sql
协程是一种用户态的轻量级线程,协程的调度彻底由用户控制。协程拥有本身的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其余地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操做栈则基本没有内核切换的开销,能够不加锁的访问全局变量,因此上下文的切换很是快。shell
个人 企鹅群 一块儿交流哦服务器
用户态线程、遇到IO主动让出控制权swoole
多个协程代码依然是串行的,无需加锁网络
开销低,只占用内存,不存在进程、线程切换开销架构
并发量大,单个进程可开启50w个协程并发
随时随地,只要想并发,就调用go建立协程异步
咱们知道线程是轻量级的进程,那么协程就是轻量级的线程。协程运行在线程之上,一个线程能够有多个协程。
咱们知道在进程遇到阻塞的时候开多一个线程在进程内部切换,避免每次都切换进程,这样能够更大力度的使用CPU分给这个进程的可以使用时间。而协程跟线程和进程的关系很相似,只不过协程是跟线程直接创建关系。
上图是多个线程之间切换的示意图,那么咱们来考虑一下,若是线程只是等待IO操做(网络或者文件),那么为何像线程重复使用进程同样来重复的使用这个线程呢?咱们把IO去掉,看看这个图是什么样子的。
去掉IO部分操做,能够看出来基本上这个并发请求应用程序代码能够在 单个线程中 运行,协程最大力度的利用了线程等待IO的时间,让程序在等待IO的时候能够执行别的业务代码。
看着像不像一个线程的执行流程,这就是协程的魅力所在,当一个协程被yield以后会被挂起,把控制权转移给线程内部的其余协程,由于是在线程上进行的切换,因此开销远远比进程和线程低不少。
当程序调用协程以后,当前协程会主动让出控制权交给同一个线程内的其余协程处理,如图所示,开发者代码中须要使用IO的时候主动让出协程的控制权给别的协程使用。
去掉IO部分再看协程的处理,直接执行的都是业务逻辑,避免遇到IO致使线程转换到等待状态,更充分的利用CPU分给这个线程的执行时间。
注意:协程并不能让任务加速进行,只能执行更多任务。
协程因为是创建在线程之上的,所以没有办法使用CPU多核心的优点,协程适合适用于IO密集运算的场景。
协程是为了提升CPU使用率,避免在线程阻塞的时候大量的线程上下文切换。
echo "1-start\n"; sleep(1); echo "1-end\n"; echo "2-start\n"; sleep(1); echo "2-end\n"; echo "3-start\n"; sleep(1); echo "3-end\n"; echo "4-start\n"; sleep(1); echo "4-end\n";
以上代码的CPU使用率仅有 1%
Swoole\Runtime::enableCoroutine(true); go(function () { echo "go1-start\n"; sleep(1); echo "go1-end\n"; }); go(function () { echo "go2-start\n"; sleep(1); echo "go2-end\n"; }); go(function () { echo "go3-start\n"; sleep(1); echo "go3-end\n"; }); go(function () { echo "go4-start\n"; sleep(1); echo "go4-end\n"; });
使用协程,成功把CPU使用率提升到了4%,这样CPU就不须要为了IO阻塞而空跑,或者进行上下文切换。以前不是说过协程不能加速吗?这里使用协程以后怎么1秒多就执行完了,跟前面的代码不同?这里获得的时间取决于最后一个协程执行结束的时间。
Swoole\Runtime::enableCoroutine(true); go(function(){ sleep(2); echo "go1\n"; }); go(function(){ sleep(1); echo "go2\n"; }); echo "main\n";
先输出:main->go2->go1
多个协程之间通信,采用Channel实现,多个协程协助完成共同的任务。
Swoole\Runtime::enableCoroutine(true); $chan = new Swoole\Coroutine\Channel(); go(function () use ($chan){ sleep(1); $chan->push(['name'=>'sunny']); }); go(function() use ($chan){ $data = $chan->pop(); print_r($data); }); echo "结束\n";
利用Swoole提供的Channel实现一个 waitGroup ,主要功能是用来等待全部协程执行完成的。
<?php class WaitGroup{ private $count; private $chan; public function __construct() { $this->chan = new Swoole\Coroutine\Channel(); } public function add(){ $this->count++; } public function done(){ $this->chan->push(true); } public function wait(){ for($i=0;$i<$this->count;$i++){ $this->chan->pop(); } } } <?php include 'waitgroup.php'; Swoole\Runtime::enableCoroutine(true); echo "start".PHP_EOL; $t = microtime(true); go(function() use ($t){ $wg = new WaitGroup(); $wg->add(); go(function() use ($t,&$wg){ echo file_get_contents("https://www.sunnyos.com/swoole.php"); echo "协程1:".(microtime(true)-$t).PHP_EOL; $wg->done(); }); $wg->add(); go(function() use ($t,&$wg){ echo file_get_contents("https://www.sunnyos.com/swoole.php"); echo "协程2:".(microtime(true)-$t).PHP_EOL; $wg->done(); }); $wg->add(); go(function() use ($t,&$wg){ echo file_get_contents("https://www.sunnyos.com/swoole.php"); echo "协程3:".(microtime(true)-$t).PHP_EOL; $wg->done(); }); $wg->wait(); echo '所有结束:'.(microtime(true)-$t).PHP_EOL; }); echo "end".PHP_EOL; echo microtime(true)-$t.PHP_EOL;
代码中:https://www.sunnyos.com/swoole.php swoole.php 的代码
<?php sleep(1); echo "My name is Sunny\n";
休眠疫苗模拟网络请求耗时
这里看看使用协程3个协程都进行了网络请求,每一个请求耗时1秒,可是在这里执行都时候三个请求执行完了仅耗时1.2秒,可是cpu都使用率却使用到了6%,这说明了协程充分的使用了cpu。
好了各位,以上就是这篇文章的所有内容了,能看到这里的人呀,都是人才。以前说过,PHP方面的技术点不少,也是由于太多了,实在是写不过来,写过来了你们也不会看的太多,因此我这里把它整理成了PDF和文档,若是有须要的能够
更多学习内容能够访问【对标大厂】精品PHP架构师教程目录大全,只要你能看完保证薪资上升一个台阶(持续更新)
以上内容但愿帮助到你们,不少PHPer在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提高,对此我整理了一些资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货须要的能够免费分享给你们,须要的能够加入个人 PHP技术交流群