参考文章 在PHP中使用协程实现多任务调度
书籍 现代操作系统,深入理解计算机系统,以及视频课程 linux核心技术
什么是协程?
协程可以理解为多任务用户操作系统中用户自行实现调度器的线程,也可以理解为由事件驱动的程序。
由你实现的调度器,调度运行的程序段就是协程,也有人叫协程为轻量级线程。
在多用户多任务操作系统如linux中,用户指的是使用该操作系统的操作者,也就是你开发者,可千万别理解成,web程序的客户端用户,我之前就遇到过这种小傻子。
php 协程具体实现组件
1. 任务管理器
2. 任务调度器
3. 协程堆栈
任务管理器以及任务调度器的初步实现
任务管理器
/**
* 任务管理器
*/
class Task
{
protected $taskId; // 任务id
protected $coroutine; // 协程对象
protected $sendValue = null; //
protected $beforeFirstYield = true; // 是否为第一次调用
public function __construct($taskId, Generator $coroutine)
{
$this->taskId = $taskId;
$this->coroutine = StackedCoroutine($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();
}
$retrieval = $this->coroutine->send($this->sendValue);
$this->sendValue = null;
return $retrieval;
}
/**
* 任务是否已结束
* @return bool
*/
public function isFinished(): bool
{
return !$this->coroutine->valid();
}
}
任务调度器
<?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);
}
}
}
}
?>
初次感受协程
<?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();