首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 協程實作合作式多任務調度器(Generator-based Coroutines)

     編輯:PHP 綜合知識

背景

PHP 5.5 引入 Generator,5.5+ 可用 yield 實作協程。協程允許函數主動暫停並稍後從暫停處繼續,配合調度器即可在單執行緒實現「合作式多任務」。

核心概念

  • Generator:可中斷函數,yield 為中斷點,雙向通訊(send() 傳值進去,yield 吐值出來)
  • Task:包裝 Generator 的輕量任務單元,含 ID、狀態、送值緩衝
  • Scheduler:任務隊列,循環取出 Task → run() → 若未結束重入隊列
  • SystemCall:任務向調度器請求服務(如建立子任務、殺掉任務、取得 ID)的統一介面

完整實作碼

<?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();

執行流程說明

  1. Scheduler 啟動,隊列含 parentTask
  2. Parent 執行到 yield getTaskId() → SystemCall 設定 sendValue = 1 → 重排隊列
  3. Parent 繼續,印出訊息,yield newTask(childTask) → SystemCall 建立子任務 ID=2 → 重排
  4. 調度器輪流執行 Parent、Child,各自 yield 讓出控制權
  5. 任務結束自動從 map/queue 移除

現代替代方案

  • Revolt / Amp / Swoole:事件迴圈 + 協程,支援非阻塞 I/O、Timer、Channel、WebSocket
  • Fiber (PHP 8.1+):原生纖程,更底層、可手動控制暫停/恢復
  • 生產環境高併發建議直接用 Swoole/OpenSwoole/FrankenPHP
本文示範純 PHP 使用者空間協程調度,適合理解原理;實務高併發請用成熟非阻塞框架。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved