PHP下的异步尝试二:初识协程

PHP下的异步尝试系列

若是你还不太了解PHP下的生成器,你能够根据下面目录翻阅segmentfault

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

多任务 (并行和并发)

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

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

因为切换执行任务的速度很是快,给外部用户的感觉就是多个任务的执行是同时进行的。异步

多进程的调度是由操做系统来实现的,进程自身不能控制本身什么时候被调度,也就是说: 进程的调度是由外层调度器抢占式实现的函数

而协程要求当前正在运行的任务自动把控制权回传给调度器,这样就能够继续运行其余任务。这与抢占式的多任务正好相反, 抢占多任务的调度器能够强制中断正在运行的任务, 无论它本身有没有意愿。若是仅依靠程序自动交出控制的话,那么一些恶意程序将会很容易占用所有 CPU 时间而不与其余任务共享。操作系统

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

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

协程能够理解为纯用户态的线程,经过协做而不是抢占来进行任务切换。code

相对于进程或者线程,协程全部的操做均可以在用户态而非操做系统内核态完成,建立和切换的消耗很是低。协程

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

咱们能够把大任务拆分红多个小任务轮流执行,若是有某个小任务在等待系统 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自动执行器),无需再手动来中断和恢复了
相关文章
相关标签/搜索