php协程 多线程,PHP中协程实现学习笔记

第一部分 什么是协程(Coroutine)

一、概念

0、先来看看Wikipedia Coroutine上的定义(Wikipedia协程中文定义):

Coroutines are computer program components that generalize subroutines for non-preemptive multitasking, by allowing execution to be suspended and resumed。

1、再来看看Wikipedia Routine定义中Coroutine的描述

Coroutine, generalized reentrant computer subroutine having multiple entry points(协程,具有多个入口点的广义可重入计算机子例程

)

2、我的理解(纯属个人理解)

先说明白routine和subrtoutine

routine: computer program, 就是程序,就是一个进程或一个线程。

subroutine: a routine inside another routine, 就是一个子程序,程序的一部分,换句话说就是一个子任务。

注:在Linux操作系统看来进程和线程都是一个task,这里为了方便阐述,以下用线程来表示线程和进程。

协程(coroutine)是一个子程序,是一类特殊的子程序。特殊子程序和普通的子程序的区别在于,普通的多个子程序构成了一个线程,这些普通子程序是不可分割的,是一个整体,不能被调用者(用户态)调用。特殊的多个子程序构成了一个线程,这些多个特殊的子程序是可以分割的,做为多个独立的子任务,被调用者(用户态的)分开调用。当然,不管是普通子程序构成的线程,还是特殊子程序构成的线程,都是被Linux操作系统调度执行。

3、区别(纯属个人理解)

多线程中,每个线程实体都是一样的;多协程中,每个协程实体都是不一样的。

多线程中,内核决定那个线程被挂起和被恢复;多协程中,程序实现者(协程调度者)来决定那个协程被挂起和被恢复。

4、误区

中文绝大多数资料都显示,协程是轻量级的线程。我认为这句话表述有误,甚至完全是错误的。线程是在进程中,一个进程中有一个或多个线程,一个线程中有多个协程。但是,协程在线程中和线程在进程中是完全不一样的。内核会调度进程中的线程,但是内核不会调度线程中的协程。用户态无法调度线程,但用户态可以调度协程。

二、进程和协程实现并行的本质区别

对于单核处理器,多进程实现多任务的原理是让操作系统给一个进程(任务)每次分配一定的CPU时间片,然后中断、让下一个任务执行一定的时间片接着再中断并继续执行下一个,如此反复。由于切换执行任务的速度非常快,给外部用户的感受就是多个任务的执行是同时进行的。

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

而协程要求当前正在运行的任务自动把控制权回传给调度器,这样就可以继续运行其他任务。这与抢占式的多任务正好相反, 抢占多任务的调度器可以强制中断正在运行的任务, 不管它自己有没有意愿。协程的调度是由协程自身主动让出控制权到外层调度器实现的。

三、协程的实现

0、进程是由操作系统调度的,所以创建一个进程很容易直接fork就可以了。但是协程是用户自己调度的,调度器需要自己实现。

第二部分 PHP中实现协程

了解PHP实现协程,我们需要首先弄懂PHP中的迭代器和生成器。可以参考本站之前的内容《PHP迭代器学习笔记》《PHP中生成器学习笔记》

注:简单理解迭代器和生成器都是用来遍历对象的,迭代器以类的形式表现,生成器以函数的形式表现。

一、通过生成器实现协程

前面说了协程需要用户自己调度任务,那么任务和调度器都需要用户自己实现。

0、任务类Task.php

class Task {

protected $taskId;

protected $coroutine;

protected $sendValue = null;

protected $beforeFirstYield = true;

public function __construct($taskId, Generator $coroutine) {

$this->taskId = $taskId;

$this->coroutine = $coroutine;

}

public function getTaskId() {

return $this->taskId;

}

public function setSendValue($sendValue) {

$this->sendValue = $sendValue;

}

public function run() {

if ($this->beforeFirstYield) {

$this->beforeFirstYield = false;

return $this->coroutine->current();

} else {

$retval = $this->coroutine->send($this->sendValue);

$this->sendValue = null;

return $retval;

}

}

public function isFinished() {

return !$this->coroutine->valid();

}

}

1、调度器类Scheduler.php

include './Task.php';

class Scheduler {

protected $maxTaskId = 0;

protected $taskMap = []; // taskId => task

protected $taskQueue;

public function __construct() {

$this->taskQueue = new SplQueue();

}

public function newTask(Generator $coroutine) {

$tid = ++$this->maxTaskId;

$task = new Task($tid, $coroutine);

$this->taskMap[$tid] = $task;

$this->schedule($task);

return $tid;

}

public function schedule(Task $task) {

$this->taskQueue->enqueue($task);

}

public function run() {

while (!$this->taskQueue->isEmpty()) {

$task = $this->taskQueue->dequeue();

$task->run();

if ($task->isFinished()) {

unset($this->taskMap[$task->getTaskId()]);

} else {

$this->schedule($task);

}

}

}

}

2、协程测试Case类TestCoroutine.php

include './Scheduler.php';

function task1() {

for ($i = 1; $i <= 10; ++$i) {

echo "This is task 1 iteration $i.\n";

yield;

}

}

function task2() {

for ($i = 1; $i <= 5; ++$i) { echo "This is task 2 iteration $i.\n"; yield; } } $scheduler = new Scheduler(); $scheduler->newTask(task1());

$scheduler->newTask(task2());

$scheduler->run();

以上运行结果

php TestCoroutine.php

This is task 1 iteration 1.

This is task 2 iteration 1.

This is task 1 iteration 2.

This is task 2 iteration 2.

This is task 1 iteration 3.

This is task 2 iteration 3.

This is task 1 iteration 4.

This is task 2 iteration 4.

This is task 1 iteration 5.

This is task 2 iteration 5.

This is task 1 iteration 6.

This is task 1 iteration 7.

This is task 1 iteration 8.

This is task 1 iteration 9.

This is task 1 iteration 10.

二、通过swoole实现协程

0、参考swoole官网Coroutine

参考:

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值