我的编程空间,编程开发者的网络收藏夹
学习永远不晚

PHP7的协程怎么实现

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

PHP7的协程怎么实现

本篇内容介绍了“PHP7的协程怎么实现”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

什么是协程

先搞清楚,什么是协程。

你可能已经听过『进程』和『线程』这两个概念。

进程就是二进制可执行文件在计算机内存里的一个运行实例,就好比你的.exe文件是个类,进程就是new出来的那个实例。

进程是计算机系统进行资源分配和调度的基本单位(调度单位这里别纠结线程进程的),每个CPU下同一时刻只能处理一个进程。

所谓的并发,只不过是看起来CPU好像同时能处理几件事情一样,对于单核CPU事实上在用很快的速度切换不同的进程。

进程的切换需要进行系统调用,CPU要保存当前进程的各个信息,同时还会使CPUCache被废掉。

所以进程切换不到非不得已就不做。

那么怎么实现『进程切换不到非不得已就不做』呢?

首先进程被切换的条件是:进程执行完毕、分配给进程的CPU时间片结束,系统发生中断需要处理,或者进程等待必要的资源(进程阻塞)等。你想下,前面几种情况自然没有什么话可说,但是如果是在阻塞等待,是不是就浪费了。

其实阻塞的话我们的程序还有其他可执行的地方可以执行,不一定要傻傻的等!

所以就有了线程。

线程简单理解就是一个『微进程』,专门跑一个函数(逻辑流)。

所以我们就可以在编写程序的过程中将可以同时运行的函数用线程来体现了。

线程有两种类型,一种是由内核来管理和调度。

我们说,只要涉及需要内核参与管理调度的,代价都是很大的。这种线程其实也就解决了当一个进程中,某个正在执行的线程遇到阻塞,我们可以调度另外一个可运行的线程来跑,但是还是在同一个进程里,所以没有了进程切换。

还有另外一种线程,他的调度是由程序员自己写程序来管理的,对内核来说不可见。这种线程叫做『用户空间线程』。

协程可以理解就是一种用户空间线程。

协程,有几个特点:

  • 协同,因为是由程序员自己写的调度策略,其通过协作而不是抢占来进行切换

  • 在用户态完成创建,切换和销毁

  • ⚠️ 从编程角度上看,协程的思想本质上就是控制流的主动让出(yield)和恢复(resume)机制

  • generator经常用来实现协程

说到这里,你应该明白协程的基本概念了吧?

PHP实现协程

一步一步来,从解释概念说起!

可迭代对象

PHP5提供了一种定义对象的方法使其可以通过单元列表来遍历,例如用foreach语句。

你如果要实现一个可迭代对象,你就要实现Iterator接口:

<?phpclass 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() {        $var = next($this->var);        echo "next: $var\n";        return $var;    }    public function valid() {        $var = $this->current() !== false;        echo "valid: {$var}\n";        return $var;    }}$values = array(1,2,3);$it = new MyIterator($values);foreach ($it as $a => $b) {    print "$a: $b\n";}

生成器

可以说之前为了拥有一个能够被foreach遍历的对象,你不得不去实现一堆的方法,yield关键字就是为了简化这个过程。

生成器提供了一种更容易的方法来实现简单的对象迭代,相比较定义类实现Iterator接口的方式,性能开销和复杂性大大降低。

<?phpfunction xrange($start, $end, $step = 1) {    for ($i = $start; $i <= $end; $i += $step) {        yield $i;    }} foreach (xrange(1, 1000000) as $num) {    echo $num, "\n";}

记住,一个函数中如果用了yield,他就是一个生成器,直接调用他是没有用的,不能等同于一个函数那样去执行!

所以,yield就是yield,下次谁再说yield是协程,我肯定把你xxxx。

PHP协程

前面介绍协程的时候说了,协程需要程序员自己去编写调度机制,下面我们来看这个机制怎么写。

0)生成器正确使用

既然生成器不能像函数一样直接调用,那么怎么才能调用呢?

方法如下:

  1. foreach他

  2. send($value)

  3. current / next...

1)Task实现

Task就是一个任务的抽象,刚刚我们说了协程就是用户空间线程,线程可以理解就是跑一个函数。

所以Task的构造函数中就是接收一个闭包函数,我们命名为coroutine

class Task{    protected $taskId;    protected $coroutine;    protected $beforeFirstYield = true;    protected $sendValue;        public function __construct($taskId, Generator $coroutine)    {        $this->taskId = $taskId;        $this->coroutine = $coroutine;    }        public function getTaskId()    {        return $this->taskId;    }        public function isFinished()    {        return !$this->coroutine->valid();    }        public function setSendValue($value)    {        $this->sendValue = $value;    }        public function run()    {        // 这里要注意,生成器的开始会reset,所以第一个值要用current获取        if ($this->beforeFirstYield) {            $this->beforeFirstYield = false;            return $this->coroutine->current();        } else {            // 我们说过了,用send去调用一个生成器            $retval = $this->coroutine->send($this->sendValue);            $this->sendValue = null;            return $retval;        }    }}

2)Scheduler实现

接下来就是Scheduler这个重点核心部分,他扮演着调度员的角色。

Class Scheduler{        protected $taskQueue;        protected $tid = 0;        public function __construct()    {                $this->taskQueue = new SplQueue();    }        public function addTask(Generator $task)    {        $tid = $this->tid;        $task = new Task($tid, $task);        $this->taskQueue->enqueue($task);        $this->tid++;        return $tid;    }        public function schedule(Task $task)    {        $this->taskQueue->enqueue($task);    }        public function run()    {        while (!$this->taskQueue->isEmpty()) {            // 任务出队            $task = $this->taskQueue->dequeue();            $res = $task->run(); // 运行任务直到 yield            if (!$task->isFinished()) {                $this->schedule($task); // 任务如果还没完全执行完毕,入队等下次执行            }        }    }}

这样我们基本就实现了一个协程调度器。

你可以使用下面的代码来测试:

<?phpfunction task1() {    for ($i = 1; $i <= 10; ++$i) {        echo "This is task 1 iteration $i.\n";        yield; // 主动让出CPU的执行权    }} function task2() {    for ($i = 1; $i <= 5; ++$i) {        echo "This is task 2 iteration $i.\n";        yield; // 主动让出CPU的执行权    }} $scheduler = new Scheduler; // 实例化一个调度器$scheduler->addTask(task1()); // 添加不同的闭包函数作为任务$scheduler->addTask(task2());$scheduler->run();

关键说下在哪里能用得到PHP协程。

function task1() {                remote_task_commit();        // 这时候请求发出后,我们不要在这里等,主动让出CPU的执行权给task2运行,他不依赖这个结果        yield;        yield (remote_task_receive());        ...} function task2() {    for ($i = 1; $i <= 5; ++$i) {        echo "This is task 2 iteration $i.\n";        yield; // 主动让出CPU的执行权    }}

这样就提高了程序的执行效率。

关于『系统调用』的实现,鸟哥已经讲得很明白,我这里不再说明。

3)协程堆栈

鸟哥文中还有一个协程堆栈的例子。

我们上面说过了,如果在函数中使用了yield,就不能当做函数使用。

所以你在一个协程函数中嵌套另外一个协程函数:

<?phpfunction echoTimes($msg, $max) {    for ($i = 1; $i <= $max; ++$i) {        echo "$msg iteration $i\n";        yield;    }} function task() {    echoTimes('foo', 10); // print foo ten times    echo "---\n";    echoTimes('bar', 5); // print bar five times    yield; // force it to be a coroutine} $scheduler = new Scheduler;$scheduler->addTask(task());$scheduler->run();

这里的echoTimes是执行不了的!所以就需要协程堆栈。

不过没关系,我们改一改我们刚刚的代码。

把Task中的初始化方法改下,因为我们在运行一个Task的时候,我们要分析出他包含了哪些子协程,然后将子协程用一个堆栈保存。(C语言学的好的同学自然能理解这里,不理解的同学我建议去了解下进程的内存模型是怎么处理函数调用)

     public function __construct($taskId, Generator $coroutine)    {        $this->taskId = $taskId;        // $this->coroutine = $coroutine;        // 换成这个,实际Task->run的就是stackedCoroutine这个函数,不是$coroutine保存的闭包函数了        $this->coroutine = stackedCoroutine($coroutine);     }

当Task->run()的时候,一个循环来分析:

function stackedCoroutine(Generator $gen){    $stack = new SplStack;    // 不断遍历这个传进来的生成器    for (; ;) {        // $gen可以理解为指向当前运行的协程闭包函数(生成器)        $value = $gen->current(); // 获取中断点,也就是yield出来的值        if ($value instanceof Generator) {            // 如果是也是一个生成器,这就是子协程了,把当前运行的协程入栈保存            $stack->push($gen);            $gen = $value; // 把子协程函数给gen,继续执行,注意接下来就是执行子协程的流程了            continue;        }        // 我们对子协程返回的结果做了封装,下面讲        $isReturnValue = $value instanceof CoroutineReturnValue; // 子协程返回`$value`需要主协程帮忙处理                if (!$gen->valid() || $isReturnValue) {            if ($stack->isEmpty()) {                return;            }            // 如果是gen已经执行完毕,或者遇到子协程需要返回值给主协程去处理            $gen = $stack->pop(); //出栈,得到之前入栈保存的主协程            $gen->send($isReturnValue ? $value->getValue() : NULL); // 调用主协程处理子协程的输出值            continue;        }        $gen->send(yield $gen->key() => $value); // 继续执行子协程    }}

然后我们增加echoTime的结束标示:

class CoroutineReturnValue {    protected $value;     public function __construct($value) {        $this->value = $value;    }         // 获取能把子协程的输出值给主协程,作为主协程的send参数    public function getValue() {        return $this->value;    }}function retval($value) {    return new CoroutineReturnValue($value);}

然后修改echoTimes

function echoTimes($msg, $max) {    for ($i = 1; $i <= $max; ++$i) {        echo "$msg iteration $i\n";        yield;    }    yield retval("");  // 增加这个作为结束标示}

Task变为:

function task1(){    yield echoTimes('bar', 5);}

这样就实现了一个协程堆栈,现在你可以举一反三了。

4)PHP7中yield from关键字

PHP7中增加了yield from,所以我们不需要自己实现携程堆栈,真是太好了。

把Task的构造函数改回去:

    public function __construct($taskId, Generator $coroutine)    {        $this->taskId = $taskId;        $this->coroutine = $coroutine;        // $this->coroutine = stackedCoroutine($coroutine); //不需要自己实现了,改回之前的    }

echoTimes函数:

function echoTimes($msg, $max) {    for ($i = 1; $i <= $max; ++$i) {        echo "$msg iteration $i\n";        yield;    }}

task1生成器:

function task1(){    yield from echoTimes('bar', 5);}

这样,轻松调用子协程。

“PHP7的协程怎么实现”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

PHP7的协程怎么实现

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

PHP7的协程怎么实现

本篇内容介绍了“PHP7的协程怎么实现”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!什么是协程先搞清楚,什么是协程。你可能已经听过『进程』和
2023-06-28

Python协程怎么实现

这篇文章主要讲解了“Python协程怎么实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Python协程怎么实现”吧!1.协程协程不是计算机提供的,计算机只提供:进程、线程。协程时人工创造
2023-07-05

go协程是怎么实现的

Go协程是通过Go语言的运行时(runtime)来实现的。当程序启动时,runtime会创建一个主线程(也称为主协程),然后在主线程上运行主函数。在Go语言中,通过关键字go可以创建一个新的协程(也称为子协程)。go关键字后面跟着一个函数调
2023-10-21

PHP中怎么实现协程

今天就跟大家聊聊有关PHP中怎么实现协程,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。多进程/线程最早的服务器端程序都是通过多进程、多线程来解决并发IO的问题。进程模型出现的最早,从
2023-06-17

python中怎么实现协程

这篇文章主要介绍了python中怎么实现协程的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇python中怎么实现协程文章都会有所收获,下面我们一起来看看吧。协程的定义协程(Coroutine),又称微线程,纤程
2023-06-29

go中协程是怎么实现的

在Go中,协程(goroutine)是通过Go语言的运行时系统(runtime)实现的。协程是一种轻量级的线程,它可以在相同的地址空间中并发执行,但是协程的调度和管理是由Go的运行时系统自动完成的,而不是由操作系统来控制。Go中的协程是基于
2023-10-20

PHP中怎么实现Swoole协程

这篇文章主要介绍了PHP中怎么实现Swoole协程的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇PHP中怎么实现Swoole协程文章都会有所收获,下面我们一起来看看吧。首先,PHP程序员已经习惯了使用多进程来实
2023-07-05

C语言怎么实现协程

这篇文章主要介绍“C语言怎么实现协程”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“C语言怎么实现协程”文章能帮助大家解决问题。协程是一种用户空间的非抢占式线程,主要用来解决等待大量的IO操作的问题。
2023-06-17

php7扩展类怎么实现

小编给大家分享一下php7扩展类怎么实现,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!在php7中,有许许多多的扩展类,今天我们就以person类为例实现doin
2023-06-20

python中的asyncio异步协程怎么实现

这篇“python中的asyncio异步协程怎么实现”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“python中的async
2023-06-30

java基于quasar怎么实现协程池

这篇文章主要讲解了“java基于quasar怎么实现协程池”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“java基于quasar怎么实现协程池”吧!业务场景:golang与swoole都拥抱
2023-07-02

GO语言怎么实现协程池管理

本篇内容介绍了“GO语言怎么实现协程池管理”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!使用channel实现协程池通过 Channel 实
2023-06-20

怎么在Python中使用gevent实现协程

怎么在Python中使用gevent实现协程?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。python是什么意思Python是一种跨平台的、具有解释性、编译性、互动性和面向对象
2023-06-14

Golang协程池gopool怎么设计与实现

这篇文章主要介绍“Golang协程池gopool怎么设计与实现”,在日常操作中,相信很多人在Golang协程池gopool怎么设计与实现问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Golang协程池gopo
2023-06-30

php7怎么实现一个简易框架

本篇内容主要讲解“php7怎么实现一个简易框架”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“php7怎么实现一个简易框架”吧!框架的核心链路是从开始的请求路由解析到控制器的分发,model的数据
2023-06-20

golang协程实现的原理是什么

Golang中的协程(goroutine)是一种轻量级的线程,由Go语言的运行时系统进行管理。协程的实现原理主要包括以下几个方面:调度器:Golang的运行时系统包含一个调度器,负责协程的创建、调度和管理。调度器使用了一种称为"工作窃取"的
2023-10-25

Linux协程的轻量级线程实现

Linux协程可以使用用户级线程库来实现,其中比较常用的有两种方式:使用coroutine库和使用libtask库。coroutine库:coroutine是一个轻量级的协程库,支持Linux和Windows系统。它提供了一组函数来创建、切
Linux协程的轻量级线程实现
2024-08-08

Go协程如何实现

本篇内容主要讲解“Go协程如何实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Go协程如何实现”吧!为什么需要协程协程的本质是将一段数据的运行状态进行打包,可以在线程之间调度,所以协程就是在单
2023-07-04

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录