27.了解协程

了解协程

进程的生命周期

            阻塞

初始化->可运行->运行中->销毁

程序与进程的关系

 

对于电脑系统来说,我们往往会开很多个程序,而这些程序的执行依赖于CPU来执行,其中每一个程序会有一个到多个进程程序的执行本质就是基于进程完成的;因为CPU并不会真正意义上的执行我们的程序,而是通过执行程序的进程来做到;

程序也可以统称为进程

进程与CPU

进程的执行,CPU并不会一直只执行同一个进程,而是会不断的切换不同的进程来执行;比如有10个进程,一个CPU,那么CPU就会不断的在10 个进程间做切换(但是切换的方式可能也并不一定友好 -- 因为谁都想先执行所以会出现进程争抢CPU资源)而CPU呢,对于每个进程的执行每次可能只会执行1秒(假设),执行完了一毫秒就会切换同时会保存切换时候的状态信息(记录在缓存中)

当然聪明的人想出了多核的方式,通过多核cpu同时在同一时间处理多个事情

协程诞生的原因

 

举例解释就是

就好比一个医院,医生给病人看病;但是每个病人看病是需要时间的(阻塞io),这个空出的时间我们的(协程)护士小姐姐 就安抚好其他的病人倒杯水什么的,

    

    进程主体在遇到阻塞,如sleep时,会让出执行权,这个执行权会切换转交给协程,协程会充分利用阻塞的这段空闲时间,以协助的方式去做其他事情。

协程体验

如上的代码中很显然就是阻塞了4秒钟才全部执行完成

 

 

出现的现象是,后面的代码直接执行完成了,而两个方法也很快的执行只是因为,在过程中体现了异步的关系没有看到时间这里我们换一种方式执行;

只需要2s执行完成

 

go(function(){    //简写版本

 

})

 

Swoole\Coroutine::create(function(){

 

});

 

  go这个函数是调用协程创建协程环境的助手函数;通过它我们可以快速创建一个协程的环境,而在闭包中就是协程空间;co是对象的简写;

Co::sleep

这是swoole封装的协程睡眠方法

对于协程来说是协助的关系,当遇到io阻塞的时候就会切换到协程中执行,如果执行的协程又遇到io阻塞就会切换给另一个协程;

Sleep: 程序会让出执行权 原生的是改变进程的状态

Co::sleep:需要在协程里面使用 , 只是记录状态,但不会真的去改变

 

什么是协程

协程,英文Coroutines,是一种比线程更加轻量级的存在。正如一个进程可以拥有多个线程一样,一个线程也可以拥有多个协程。

最重要的是,协程不被操作系统内核所管理,而完全是由程序所控制(也就是在用户态执行)。这样带来的好处就是性能得到了很大的提升,不会像线程切换那样消耗资源。

 

协程(Coroutine)也叫用户级线程,其通过协作而不是抢占来进行切换。相对于进程或者线程,协程所有的操作都可以在用户态完成,创建和切换的消耗更低为什么说在用户态执行会消耗更低,性能可以提升

 

Swoole可以为每一个请求创建对应的协程,根据IO的状态来合理调度协程

线程和进程的调度是由操作系统来调控, 而协程的调度由用户自己调控。

 

协程调度器可以在协程A进入IO阻塞操作, 将该协程挂起,把当前的栈信息 StackA 保存下来,然后切换到协程B,等到协程A的IO操作返回时, 再根据保存的信息Stack A 切回到之前的协程A当时的状态。

协程相对于事件驱动是一种更先进的高并发解决方案,把复杂的逻辑和异步都封装在底层,让程序员在编程时感觉不到异步的存在。

 

1.在某个特定的状态主动让出cpu的执行权限(中断执行)

2.一旦程序状态改变,能够恢复程序的执行

 

一般普通的执行流程:

 

协程的执行方式:

协程好处

1.开发者可以无感知同步的代码达到异步IO的效果和性能,避免了传统异步回调所带来的离散的代码逻辑和陷入多层回调中导致代码无法维护。

2.同时由于swoole是在底层封装了协程,所以对比传统的php层协程框架,开发者不需要使用yield关键词来标识一个协程IO操作,所以不再需要对yield的语义进行深入理解以及对每一级的调用都修改为yield,这极大的提高了开发效率

 

注意:

1.Swoole的协程在底层实现上是单线程的,因此同一时间只有一个协程在工作,协程的执行是串行的。这与线程不同,多个线程会被操作系统调度到多个CPU执行。

2.协程遇到io才会切换,单线程遇到io或执行时间过长就会被迫交出cpu执行权限,切换其他线程运行

 

体验项目

模拟实现协程

1.在某个特定的状态主动让出cpu的执行权限(中断执行)

2.一旦程序状态改变,能够恢复程序的执行

 

不过这个函数如果生成的数组太大是会存在问题的:

<?php

foreach (range(0, 100000000) as $key => $value) {

// code...

}

?>

生成器

当我们需要一次性返回需要的大文件,或者数组,通过遍历生成的就可以用生成器(一般也不会经常用到,了解就好) 这里我们通过生成器就是生成这个数组

<?php

function xrange($start, $end, $stop = 0)

{

for ($i=$start; $i < $end; $i++) {

yield $i;

}

}

foreach (xrange(0, 100000000) as $key => $value) {

// code...

}

?>

然后执行;发现可以遍历并且没有超出最大内存

然后进行var_dump( xrange(0,100000000))之后发现这并不是一组数据而是一个对象

这是一个生成器对象,这个对象实现了Iterator迭代器,迭代器(可以遍历类)

那么为什么没有超过内存呢?实际上它每次返回的是一个值,通过yield借用迭代器的方法获取到我们需要的值,而没有一次性获取

 

yield -》生成器 -》迭代器

迭代器的好处:

从容器中一个一个取值,会把所有的值都取到

节省内存空间(迭代器并不会占用太大的空间,而是随着循环每次生成一个)

 

大量数据的导出

在上面的代码中 xrange会返回一个生成器,而在代码执行的时候yield会起到一个中断的效果,会先取出一个值然后直接返回出来停止循环,当需要第二个的时候就会去获取第二个值然后又停止循环;也就是说在数据获取的时候并不是一次性获取,而是一步一步的获取

<?php

function xrange($start, $end, $stop = 0)

{

for ($i=$start; $i < $end; $i++) {

yield $i;

}

}

 

// 演示获取

 

$ger = xrange(1, 10);

var_dump($ger->current());// 获取目前的值

while (true) {

$res = $ger->send(1);// 恢复执行获取下一个值,然后又停止

var_dump($res);

sleep(1);

}

 

Yield只记录当前循环的,用完一次就扔了

 

$iter = xrange(1,10);

$iter->current();获取当前的值

$iter->send();恢复执行获取下一个值

模拟协程的任务切换

我们目前也是期望可以实现像swoole的协程那样遇到了阻塞就会切换到另一个协程,然后进行交替的执行;

就会出现循环执行, 对于while内容专业点说就是一个任务;swoole也有;需要注意到的是我们只是做到任务切换,但是并不是减少时间

 

如果希望真的减少时间,就比较难了,因为需要监控和异步执行;而直接用传统的方式去整呢,挺麻烦的还要自己去监听进程状态;这里我们以请求连接的方式实现以下:

 

协程实例体验

<?php

sleep(5);

echo "睡上5秒钟";

?>

 

正常的curl代码,模拟协程的异步

 

原生的php测试

 

解释:就是咋们的系统在不断切换两个任务执行,这是最简单实现方式

 

原理就是在某一个特定时间发生了阻塞的时候,就会中断执行,也就是中断while循环;然后就会进入到另一个方法中

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值