PHP下的异步尝试系列

如果你还不太了解PHP下的生成器,你可以根据下面目录翻阅

  1. PHP下的异步尝试一:初识生成器
  2. PHP下的异步尝试二:初识协程
  3. PHP下的异步尝试三:协程的PHP版thunkify自动执行器
  4. PHP下的异步尝试四:PHP版的Promise
  5. [PHP下的异步尝试五:PHP版的Promise的继续完善]

多任务 (并行和并发)

在讲协程之前,先谈谈多进程、多线程、并行和并发。

对于单核处理器,多进程实现多任务的原理是让操作系统给一个任务每次分配一定的 CPU 时间片,然后中断、让下一个任务执行一定的时间片接着再中断并继续执行下一个,如此反复。

由于切换执行任务的速度非常快,给外部用户的感受就是多个任务的执行是同时进行的。

多进程的调度是由操作系统来实现的,进程自身不能控制自己何时被调度,也就是说: 进程的调度是由外层调度器抢占式实现的

而协程要求当前正在运行的任务自动把控制权回传给调度器,这样就可以继续运行其他任务。这与抢占式的多任务正好相反, 抢占多任务的调度器可以强制中断正在运行的任务, 不管它自己有没有意愿。如果仅依靠程序自动交出控制的话,那么一些恶意程序将会很容易占用全部 CPU 时间而不与其他任务共享。

协程的调度是由协程自身主动让出控制权到外层调度器实现的

回到刚才生成器实现 xrange 函数的例子,整个执行过程的交替可以用下图来表示:

协程可以理解为纯用户态的线程,通过协作而不是抢占来进行任务切换。

相对于进程或者线程,协程所有的操作都可以在用户态而非操作系统内核态完成,创建和切换的消耗非常低。

简单的说协程 就是提供一种方法来中断当前任务的执行,保存当前的局部变量,下次再过来又可以恢复当前局部变量继续执行。

我们可以把大任务拆分成多个小任务轮流执行,如果有某个小任务在等待系统 IO,就跳过它,执行下一个小任务,这样往复调度,实现了 IO 操作和 CPU 计算的并行执行,总体上就提升了任务的执行效率,这也便是协程的意义

多线程
在单核下,多线程必定是并发的;
不过现在的统一进程的多线程是可以运行在多核CPU下,所以可以是并行的

并发(Concurrency)

是指能处理多个同时性活动的能力,并发事件之间不一定要同一时刻发生。

并行(Parallesim)

是指同时发生的两个并发事件,具有并发的含义,而并发则不一定并行。
多个操作可以在重叠的时间段内进行。

并行和并发区别

并发指的是程序的结构,并行指的是程序运行时的状态
并行一定是并发的,并行并发设计的一种
单线程永远无法达到并行状态

协程

协程的支持是在生成器的基础上, 增加了可以回送数据给生成器的功能(调用者发送数据给被调用的生成器函数).
这就把生成器到调用者的单向通信转变为两者之间的双向通信.
我们在上篇文章已经讲过了send方法, 下面让我们理解下协程

同步代码

在没有涉及到异步执行代码之前,我们的代码都是这样的


function printNum($max, $caller)
{
for ($i=0; $i<$max; $i++ ) {
echo "调度者:" . $caller . " 打印:" . $i . PHP_EOL;
}
} printNum(3, "caller1");
printNum(3, "caller2"); # output
调度者:caller1 打印:0
调度者:caller1 打印:1
调度者:caller1 打印:2
调度者:caller2 打印:0
调度者:caller2 打印:1
调度者:caller2 打印:2

使用协程后改进的代码

初稿,手动调整生成器执行


# 本代码手动调整了进程执行代码的顺序,当然本代码实现不用协程也可以,只是利用本流程说明协程作用
# 生成器给了我们函数中断,协程[生成器send]给了我们重新唤起生成器函数的能力
function printNumWithGen($max)
{
for ($i=0; $i<$max; $i++ ) {
$res = yield $i;
echo $res;
}
} $gen1 = printNumWithGen(3);
$gen2 = printNumWithGen(3); // 手动执行caller1 再 caller2
$gen1->send("调度者: caller1 打印:" . $gen1->current() . PHP_EOL);
$gen2->send("调度者: caller2 打印:" . $gen2->current() . PHP_EOL); // 手动执行caller1 再 caller2
$gen1->send("调度者: caller1 打印:" . $gen1->current() . PHP_EOL);
$gen2->send("调度者: caller2 打印:" . $gen2->current() . PHP_EOL); // 手动执行caller2 再 caller1
$gen2->send("调度者: caller2 打印:" . $gen2->current() . PHP_EOL);
$gen1->send("调度者: caller1 打印:" . $gen1->current() . PHP_EOL); # output
调度者: caller1 打印:0
调度者: caller2 打印:0
调度者: caller1 打印:1
调度者: caller2 打印:1
调度者: caller2 打印:2
调度者: caller1 打印:2

总结


上面案例应该让大家理解了协程设计的意义和如何使用协程 那么接下去我们为我们的协程自动一个自动调度器(Co自动执行器),无需再手动来中断和恢复了

原文地址:https://segmentfault.com/a/1190000016472108

PHP下的异步尝试二:初识协程的更多相关文章

  1. PHP下的异步尝试三:协程的PHP版thunkify自动执行器

    PHP下的异步尝试系列 如果你还不太了解PHP下的生成器和协程,你可以根据下面目录翻阅 PHP下的异步尝试一:初识生成器 PHP下的异步尝试二:初识协程 PHP下的异步尝试三:协程的PHP版thunk ...

  2. PHP下的异步尝试一:初识生成器

    PHP下的异步尝试系列 PHP下的异步尝试一:初识生成器 PHP下的异步尝试二:初识协程 PHP下的异步尝试三:协程的PHP版thunkify自动执行器 PHP下的异步尝试四:PHP版的Promise ...

  3. PHP下的异步尝试四:PHP版的Promise

    PHP下的异步尝试系列 如果你还不太了解PHP下的生成器和协程,你可以根据下面目录翻阅 PHP下的异步尝试一:初识生成器 PHP下的异步尝试二:初识协程 PHP下的异步尝试三:协程的PHP版thunk ...

  4. Kotlin Coroutine(协程): 二、初识协程

    @ 目录 前言 一.初识协程 1.runBlocking: 阻塞协程 2.launch: 创建协程 3.Job 4.coroutineScope 5.协程取消 6.协程超时 7.async 并行任务 ...

  5. 异步等待的 Python 协程

    现在 Python 已经支持用协程进行异步处理.但最近有建议称添加协程以全面完善 Python 的语言结构,而不是像现在这样把他们作为生成器的一个类型.此外,两个新的关键字---异步(async)和等 ...

  6. 异步时代-java的协程路在何方

    面试官:你知道协程吗? 你:订机票的那个吗,我常用. 面试官:行,你先回去吧,到时候电话联系 ........ 很尴尬,但是事实是,很大一部分的程序员不知道协程是啥玩意,更大一部分的程序员,项目中没用 ...

  7. Py修行路 python基础 (十二) 协程函数应用 列表生成式 生成器表达式

    一.知识点整理: 1.可迭代的:对象下有_iter_方法的都是可迭代的对象 迭代器:对象._iter_()得到的结果就是迭代器 迭代器的特性: 迭代器._next_() 取下一个值 优点: 1.提供了 ...

  8. Swoole 中使用 TCP 异步服务器、TCP 协程服务器、TCP 同步客户端、TCP 协程客户端

    TCP 异步风格服务器 异步风格服务器通过监听事件的方式来编写程序.当对应的事件发生时底层会主动回调指定的函数. 由于默认开启协程化,在回调函数内部会自动创建协程,遇到 IO 会产生协程调度,异步风格 ...

  9. python进阶(二) 多进程+协程

    我们大多数的时候使用多线程,以及多进程,但是python中由于GIL全局解释器锁的原因,python的多线程并没有真的实现 实际上,python在执行多线程的时候,是通过GIL锁,进行上下文切换线程执 ...

随机推荐

  1. nyoj13-Fibonacci数

    Fibonacci数 时间限制:3000 ms  |  内存限制:65535 KB 难度:1 描述 无穷数列1,1,2,3,5,8,13,21,34,55...称为Fibonacci数列,它可以递归地 ...

  2. 2019-04-03 SQL Group By某列,预先对该列进行一个预处理,提炼出共有的信息,即关键字case when 列名什么条件 then 赋值 else 赋值 end as 新列名

    select sum(发行金额) from( select PoolNameFormat,count(cast(ItemValue as decimal(19,4))) as 发行笔数,sum(cas ...

  3. Mybatis拦截器执行过程解析

    上一篇文章 Mybatis拦截器之数据加密解密 介绍了 Mybatis 拦截器的简单使用,这篇文章将透彻的分析 Mybatis 是怎样发现拦截器以及调用拦截器的 intercept 方法的 小伙伴先按 ...

  4. 完整的html+css+javascript实现跟随鼠标移动显示选中效果

    1,显示效果: 2,html结构 <div class="process_list-lpu"> <div class="process_line&quo ...

  5. 【Web API系列教程】3.4 — 实战:处理数据(处理实体关系)

    前言 本部分描写叙述了EF怎样载入相关实体的细节,而且怎样在你的模型类中处理环形导航属性.(本部分预备了背景知识,而这不是完毕这个教程所必须的.你也能够跳到第五节) 预载入和延迟载入 预载入和延迟载入 ...

  6. 解决was6版本号过期问题

    原创作品.出自 "深蓝的blog" 博客,欢迎转载.转载时请务必注明出处.否则追究版权法律责任. 深蓝的blog:http://blog.csdn.net/huangyanlong ...

  7. HDU 3709

    真是跪了,一看范围就不会往枚举的方向想,没想到真用枚举加剪枝了...->——-> 解释一下代码中的上限: 例如4567,当枚举最高位时,很明显不能超过4,所以有上限,但当最高位为3以下时, ...

  8. 假设让我又一次设计一款Android App

    转载请注明出处: 本文来自aspook的博客:http://blog.csdn.net/ahence/article/details/47154419 开发工具的选择 开发工具我将选用Android  ...

  9. 【ruby项目,语言提交检查(一)】怎样高速学习ruby ?

    怎样高速学习ruby ? 学习语言最快的思路. 变量,常量,变量类型,操作符. 逻辑语句如 if, else, switch, for, foreach, do while, break, 等等.要学 ...

  10. C# 跨线程调用form控件技巧及byte[]与string型相互转换

    跨线程调用form控件技巧 private delegate void MethodSocket(object obj);//使用托管 ss = "OK"; this.BeginI ...