PHP 新 RFC 即将到来!异步调度器正式进入 PHP 核心

一项新的 RFC 提案即将提出,该提案旨在进一步推进异步执行功能在 PHP 核心中的应用。
什么是调度器?它为什么是必要的?
调度器是负责管理协程生命周期的组件。它负责创建、调度、切换和销毁协程,能够检测死锁情况,并驱动应用程序的主事件循环。换句话说,调度器充当了协程的主管角色。
Fiber API 通过依赖非对称上下文切换的方式来弥补缺乏此类管理器的缺陷。虽然这种方法功能上可行,但复杂程度却相当高。
在多个 Fiber 之上运行时,每个协程都必须执行一次额外的上下文切换,以在当前正在执行的 Fiber 与所有其他 Fiber 所源自的根 Fiber 之间切换。因为 Fiber 并不允许任意的上下文切换。虽然 Revolt 试图通过优化来让多个协程共享相同的 Fiber,但它无法消除这一根本限制。
在设计层面,调度器完全避免了这个问题。其 API 可以概括如下(伪代码):
interface SchedulerAPI {
public function newCoroutine(callable $function): Coroutine;
public function suspend(): void;
public function enqueue(Coroutine $coro): void;
}
只需要使用三种核心方法即可:
newCoroutine()创建一个新的协程。enqueue()函数将一个协程放入调度器的就绪队列中。suspend()会将执行任务返回给调度器。
TrueAsync 是如何使用调度器的
在内部,TrueAsync API 是建立在 Scheduler API 之上的。
这就是 spawn()函数的运作方式:
function spawn(callable $fun): Coroutine {
$coro = $scheduler->newCoroutine($fun);
$scheduler->enqueue($coro);
}
这个函数只是创建一个协程,然后安排其执行。当协程暂停时:
function suspend(): void
{
$next = $this->queue->dequeue();
$this->switchTo($next);
}
调度器从就绪队列中取出下一个待执行的协程,并将执行任务转移到该协程上。
为什么调度器应该被包含在 PHP 的核心代码中呢?
有几个重要的原因。
从历史上看,PHP 与其他大多数编程语言的不同之处在于它并没有明确的入口点。这里没有名为main()的函数,而是直接以某个脚本文件(如 index.php)的执行开始。
例如:
<?php echo "php start\n"; echo "php end\n";
打印完成后,执行自然就会结束:
php start php end
然而,一旦引入了协程机制,这种模型就不再适用了。
<?php
echo "php start\n";
spawn(fn() => print("here\n"));
echo "php end\n";
打印
php start php end here
即使主执行流程已经结束,PHP 进程仍然会继续运行,因为还存在可执行的协程。
这正是预期中的行为:主要的执行流程本身应该被表示为一个协程。
从概念上讲,像这样的代码:
<?php echo "start\n";
应该隐含地执行与以下操作相当的操作:
createCoroutine(...)
这使得 PHP 的执行模型变得标准化,实际上让 PHP 成为一种具有内置并发性的语言。
将主要执行流程视为另一个协程是这份 RFC 的主要目标之一。
异步标准库
RFC 的另一个主要目标是让 PHP 标准库能够直接支持异步执行功能。
允许现有的 PHP 函数能够在异步代码中以非阻塞方式运行,这为开发者节省了无数时间,同时也为 PHP 生态系统带来了全新的功能。
让我们来看看 sleep()函数的以下伪代码实现:
php_async_sleep(int $seconds)
{
$coro = current_coroutine();
async_io_timer_new(
$seconds * 1000,
/* period */ 0,
function () use ($coro) {
$scheduler->enqueue($coro);
}
);
$scheduler->suspend();
}
这个实现展示了如何利用 Reactor API 使得sleep()函数变为异步操作。
有两个重要的步骤:
- 创建一个计时器,并注册一个回调函数,在计时器到期时重新排队并执行暂停的协程。
- 通过调用
suspend()函数,明确地将执行权交给调度器处理。
要使这一功能生效,PHP 内核本身必须能够理解调度器接口。
一旦这个基础结构建立起来之后,所有的阻塞 I/O 操作都可以被修改,使其能够在原本需要等待的地方暂停执行。这正是 TrueAsync 在 PHP 核心代码中实现的机制——通过在每个阻塞 I/O 操作点插入协程上下文切换,从而实现这一功能。
超越调度
调度器 API 还对几个核心引擎组件进行了重要的修改。
例如
- 垃圾收集现在能够意识到协程的存在;
- 救助操作的执行是在其自己的协程中进行的;
- 该引擎表现为一个统一的并发运行环境,而不是由多个独立且同步的子系统组成的系统。
执行上下文
仅使用协程还不足以实现完全并发的运行环境。
许多 PHP 功能依赖于某种执行状态,而该执行状态必须始终与特定的协程相关联。
一个很好的例子就是ob_start()函数,它会将输出数据累积到一个缓冲区中,之后再进行处理或稍后返回。
尝试将ob_start()与 Fibers 或 AMPHP 一起使用会导致不可预测的行为,许多依赖于全局执行状态的 API 也会产生类似的问题。
为了实现确定性行为,PHP 内核需要一种能够在切换协程时保持执行状态的内部机制。
这种机制被称为协程上下文。
调度器 API 定义了两种协程上下文:
- 内部上下文,适用于高层管理人员的使用以及核心功能的开发。
- 用户上下文,适用于各种框架和应用场景。
用户上下文使得框架和服务能够将任意状态与当前执行的协程关联起来——例如,请求范围内的数据。
协程上下文是使现有框架能够顺利适应异步 PHP 运行环境的核心机制。
以上关于PHP 新 RFC 即将到来!异步调度器正式进入 PHP 核心的文章就介绍到这了,更多相关内容请搜索码云笔记以前的文章或继续浏览下面的相关文章,希望大家以后多多支持码云笔记。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 admin@mybj123.com 进行投诉反馈,一经查实,立即处理!
重要:如软件存在付费、会员、充值等,均属软件开发者或所属公司行为,与本站无关,网友需自行判断
码云笔记 » PHP 新 RFC 即将到来!异步调度器正式进入 PHP 核心
微信
支付宝