概念

咱们知道多进程和多线程是实现并发的有效方式。但多进程的上下文切换资源开销太大;多线程开销相比要小很多,也是现在主流的做法,但其的控制权在内核,从而使用户(程序员)失去了对代码的控制,而且线程的上下文切换也是有一定开销的。 这时为了解决以上问题,"协程"(coroutine)的概念就产生了。你可以将协程理解为更轻量级的线程。这种线程叫做“用户空间线程“。协程,有下面两个特点:

  1. 协同。因为是由程序员自己写的调度策略,其通过协作而不是抢占来进行切换
  2. 在用户态完成创建,切换和销毁

PHP对协程的支持是在迭代生成器的基础上, 增加了可以回送数据给生成器的功能(调用者发送数据给被调用的生成器函数)。 这就把生成器到调用者的单向通信转变为两者之间的双向通信。

迭代器

迭代器的概念这里就不赘述了。下面看看我们自己实现的一个迭代器。

 class MyIterator implements Iterator
{
private $var = array(); public function __construct($array)
{
if (is_array($array)) {
$this->var = $array;
}
} public function rewind() { // 第一次迭代时候会执行(或调用该方法的时候),后面的迭代将不会执行。
echo "rewinding\n";
reset($this->var);
} public function current() {
$var = current($this->var);
echo "current: $var\n";
return $var;
} public function key() {
$var = key($this->var);
echo "key: $var\n";
return $var;
} public function next() { // 最后执行,就是执行完下面sleep(2)后再执行。(执行了next本次迭代才算结束)
$var = next($this->var);
echo "next: $var\n";
return $var;
} public function valid() { // 当valid返回false的时候迭代结束
$var = $this->current() !== false;
echo "valid: {$var}\n";
return $var;
}
} $values = array(1,2,3,4);
$it = new MyIterator($values); foreach ($it as $a => $b) { // 进行迭代(每次迭代,会依次执行以下方法: rewind(特别之处见上面解释), valid, current, key, next)
print "=====\n";
sleep(2);
}

输出:

rewinding
current: 1 // 因为valid里面调用了current, 这里current出来一次
valid: 1
current: 1
key: 0
=====
next: 2
current: 2
valid: 1
current: 2
key: 1
=====
next: 3
current: 3
valid: 1
current: 3
key: 2
=====
next: 4
current: 4
valid: 1
current: 4
key: 3
=====
next:
current:
valid: // valid返回false,迭代结束

生成器

有了yeild的方法就是一个生成器(生成器实现了Iterator接口,即一个生成器有迭代器的特点)。生成器的实现如下:

 function xrange($start, $end, $step = 1) {
for ($i = $start; $i <= $end; $i += $step) {
echo $i . "\n";
yield;
}
} // foreach方式
foreach (xrange(1, 10) as $num) { } $gene = xrange(1, 10); // gene就是一个生成器对象
// current
$gene->current(); // 打印1
// next
$gene->next();
$gene->current() // 打印2

输出:

1
2
3
4
5
6
7
8
9
10
1
2

生成器各方法详解可看文档: http://php.net/manual/zh/class.generator.php

注意:

生成器不能像函数一样直接调用,调用方法如下:

1. foreach他

2. send($value)

3. current / next...

yield

yield的语法很灵活,我们用下面的例子,让大家能明白yield语法的使用。

用例1: 让出cpu执行权

 function task1 () {
for ($i = 1; $i <= 10; ++$i) {
echo "This is task 1 iteration $i.\n";
yield;// 遇到yield就会主动让出CPU的执行权;
}
} $a = task1();
$a->current(); // 执行第一次迭代
$a->send(1); // 唤醒当时让出CPU执行权的yield

输出:

This is task 1 iteration 1.
This is task 1 iteration 2.

用例2: yield的返回

 // yield返回
function task2 () {
for ($i = 1; $i <= 10; ++$i) {
echo "This is task 2 iteration $i.\n";
yield "lm$i"; // 遇到yield就会主动让出CPU的执行权,for暂停执行, 然后返回"lm"。放在yield后面的值就是返回值
}
} $a = task2();
$res = $a->current(); // 第一次迭代, 遇到yield返回
var_dump($res);
$res = $a->send(1); // 唤醒yield, for继续执行,遇到yield返回。
var_dump($res);

输出:

This is task 2 iteration 1.
string(3) "lm1"
This is task 2 iteration 2.
string(3) "lm2"

用例3: yield接收值

 function task3 () {
for ($i = 1; $i <= 10; ++$i) {
echo "This is task 3 iteration $i.\n";
$getValue = yield;// 遇到yield就会主动让出CPU的执行权;send后,将send值赋值给getValue
echo $getValue . " ";
}
} $a = task3();
$a->current();
$a->send("aa"); // 唤醒yield,并将"aa"值赋值给$getValue变量

输出:

This is task 3 iteration 1.
aa This is task 3 iteration 2.

用例4: yeild接收和返回写在一起

function task4 () {
for ($i = 1; $i <= 10; ++$i) {
echo "This is task 4 iteration $i.\n";
$ret = yield "lm$i"; // yield, 然后返回lm$i; 当send时,将send过来的值赋值给$ret;
echo $ret;
}
} $a = task4();
var_dump($a->current()); // 返回lm1
var_dump($a->send("hhh ")); // 先唤醒yield, 将"hhh "赋值给$ret,再返回lm2
var_dump($a->send("www ")); // 先唤醒yield, 将"www "赋值给$ret,再返回lm3

输出:

This is task 4 iteration 1.
string(3) "lm1"
hhh This is task 4 iteration 2.
string(3) "lm2"
www This is task 4 iteration 3.
string(3) "lm3"

结语:

如果你有看过鸟哥的这篇文章http://www.laruence.com/2015/05/28/3038.html,应该对协程有个深刻的认识。但里面内容更适合中高级PHP工程师看,而且还得具备一定的操作系统的知识,所以我在此基础上用更通俗的方式,阐明一下PHP的协程概念。协程很强大的功能但相对比较复杂, 也比较难被理解。个人目前还没有遇到合适的场景来使用PHP协程,不过我猜测,由于可以在用户层面实现多并发,所以多用于CLI模式下的web服务开发,比如Golang的goroutine并不是线程,而是协程。还有yield有双向通信的功能,所以还可以实现异步服务,但需要自己写调度器,比如鸟哥这篇博客里面的非阻塞IOweb服务器就是靠协程实现异步了实现的。

以上内容如果有错误还请留言交流。

PHP协程入门详解的更多相关文章

  1. Lua的协程和协程库详解

    我们首先介绍一下什么是协程.然后详细介绍一下coroutine库,然后介绍一下协程的简单用法,最后介绍一下协程的复杂用法. 一.协程是什么? (1)线程 首先复习一下多线程.我们都知道线程——Thre ...

  2. Python中Paramiko协程方式详解

    什么是协程 协程我们可以看做是一种用户空间的线程. 操作系统对齐存在一无所知,需要用户自己去调度. 比如说进程,线程操作系统都是知道它们存在的.协程的话是用户空间的线程,操作系统是不知道的. 为什么要 ...

  3. Android中的Coroutine协程原理详解

    前言 协程是一个并发方案.也是一种思想. 传统意义上的协程是单线程的,面对io密集型任务他的内存消耗更少,进而效率高.但是面对计算密集型的任务不如多线程并行运算效率高. 不同的语言对于协程都有不同的实 ...

  4. Linq之旅:Linq入门详解(Linq to Objects)

    示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...

  5. SQL注入攻防入门详解

    =============安全性篇目录============== 本文转载 毕业开始从事winfrm到今年转到 web ,在码农届已经足足混了快接近3年了,但是对安全方面的知识依旧薄弱,事实上是没机 ...

  6. SQL注入攻防入门详解(2)

    SQL注入攻防入门详解 =============安全性篇目录============== 毕业开始从事winfrm到今年转到 web ,在码农届已经足足混了快接近3年了,但是对安全方面的知识依旧薄弱 ...

  7. Quartz 入门详解

    Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用.Quartz可以用来创建简单或为运行十个,百个, ...

  8. Redis快速入门详解

    Redis入门详解 Redis简介 Redis安装 Redis配置 Redis数据类型 Redis功能 持久化 主从复制 事务支持 发布订阅 管道 虚拟内存 Redis性能 Redis部署 Redis ...

  9. [转]SQL注入攻防入门详解

    原文地址:http://www.cnblogs.com/heyuquan/archive/2012/10/31/2748577.html =============安全性篇目录============ ...

随机推荐

  1. 《R语言入门与实践》第六章:R 的环境系统

    前言 这一章在对象的基础之上,讲解了对象所处的环境,进一步讲了环境对对象的作用,以及如何使用环境.结构如下: 环境的定义和操作 环境的规则 制作闭包 环境 R 环境的定义 在 R 中,每一个数据对象都 ...

  2. .class 缓存

    项目用的是Ant. 场景: Class A{ private static final String HHH="hello"; } Class B{ public void met ...

  3. LRU算法的Java实现

    LRU全称是Least Recently Used,即最近最久未使用的意思. LRU算法的设计原则是:如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小.也就是说,当限定的空间已 ...

  4. Linux基础命令mkdir

    mkdir  /home/ceshi   home目录下创建测试目录 mkdir  -p  /home/ceshi1/ceshi home目录下递归创建 mkdir     /home/ceshi2  ...

  5. BUAAOO-First-Summary

    目录 homework & class & trainning : 两次上机.三次作业.四周课堂 code analysis & review : 为什么我没有bug 黑盒测试 ...

  6. 采用Tensorflow内部函数直接对模型进行冻结

    # enhance_raw.py # transform from single frame into multi-frame enhanced single raw from __future__ ...

  7. leetcode 230 二叉搜索树中第K小的元素

    方法1:统计每个节点的子节点数目,当k>左子树节点数目时向左子树搜索,k=左子树节点数目时返回根节点,否则向右子树搜索. 方法2:递归中序遍历,这里开了O(n)空间的数组. class Solu ...

  8. windows下Xshell远程访问虚拟机

    下载Xshell 5软件在windows下安装 安装好后Xshell 5启动软件 下一步,检查虚拟机,配置是否正确 下一步,设置网络,保障虚拟机系统能够连接网络 下一步,进入虚拟机系统,检查虚拟机网络 ...

  9. java8 简便的map和list操作

    如果你看到这篇文章,说明你对java繁琐的list和map操作产生了厌烦.在java中,频繁的操作基本上是获取到对象list,然后根据某个属性或者某几个属性的值,把list转为map,然后遍历其他对象 ...

  10. learning makefile call func