PHP 5.5 引入 Generator,5.5+ 可用 yield 實作協程。協程允許函數主動暫停並稍後從暫停處繼續,配合調度器即可在單執行緒實現「合作式多任務」。
yield 為中斷點,雙向通訊(send() 傳值進去,yield 吐值出來)run() → 若未結束重入隊列<?php
// ---------- Task ----------
class Task {
protected $taskId, $coroutine, $sendValue = null, $beforeFirstYield = true;
public function __construct($taskId, Generator $coroutine) {
$this->taskId = $taskId;
$this->coroutine = $coroutine;
}
public function getTaskId() { return $this->taskId; }
public function setSendValue($v) { $this->sendValue = $v; }
public function run() {
if ($this->beforeFirstYield) {
$this->beforeFirstYield = false;
return $this->coroutine->current();
}
$ret = $this->coroutine->send($this->sendValue);
$this->sendValue = null;
return $ret;
}
public function isFinished() { return !$this->coroutine->valid(); }
}
// ---------- Scheduler ----------
class Scheduler {
protected $maxTaskId = 0, $taskMap = [], $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 killTask($tid) {
if (!isset($this->taskMap[$tid])) return false;
unset($this->taskMap[$tid]);
foreach ($this->taskQueue as $i => $t) {
if ($t->getTaskId() === $tid) { unset($this->taskQueue[$i]); break; }
}
return true;
}
public function run() {
while (!$this->taskQueue->isEmpty()) {
$task = $this->taskQueue->dequeue();
$retval = $task->run();
if ($retval instanceof SystemCall) {
$retval($task, $this); // 執行系統呼叫
continue;
}
if ($task->isFinished()) unset($this->taskMap[$task->getTaskId()]);
else $this->schedule($task);
}
}
}
// ---------- SystemCall ----------
class SystemCall {
protected $callback;
public function __construct(callable $cb) { $this->callback = $cb; }
public function __invoke(Task $task, Scheduler $scheduler) {
return ($this->callback)($task, $scheduler);
}
}
// ---------- 內建系統呼叫 ----------
function getTaskId() {
return new SystemCall(function(Task $t, Scheduler $s) {
$t->setSendValue($t->getTaskId());
$s->schedule($t);
});
}
function newTask(Generator $coroutine) {
return new SystemCall(function(Task $t, Scheduler $s) use ($coroutine) {
$t->setSendValue($s->newTask($coroutine));
$s->schedule($t);
});
}
function killTask($tid) {
return new SystemCall(function(Task $t, Scheduler $s) use ($tid) {
$t->setSendValue($s->killTask($tid));
$s->schedule($t);
});
}
// ---------- 使用範例 ----------
function childTask($max) {
$tid = (yield getTaskId());
for ($i=1; $i<=$max; $i++) {
echo "Child $tid: iteration $i\n";
yield;
}
}
function parentTask() {
$tid = (yield getTaskId());
for ($i=1; $i<=3; $i++) {
echo "Parent $tid: spawning child $i\n";
yield newTask(childTask(3));
yield;
}
}
$scheduler = new Scheduler();
$scheduler->newTask(parentTask());
$scheduler->run();
parentTaskyield getTaskId() → SystemCall 設定 sendValue = 1 → 重排隊列yield newTask(childTask) → SystemCall 建立子任務 ID=2 → 重排yield 讓出控制權本文示範純 PHP 使用者空間協程調度,適合理解原理;實務高併發請用成熟非阻塞框架。