Amphp之事件循环

9 篇文章 0 订阅
9 篇文章 0 订阅

事件循环(Event Loop)

人们可能会惊讶地发现 PHP 标准库已经拥有我们编写事件驱动和非阻塞应用程序所需的一切。只有当我们要求它同时轮询数千个文件描述符以进行 IO 活动时,我们才会在这方面达到原生 PHP 功能的限制。但是,即使在这种情况下,问题也不在于 PHP,而在于底层系统select()调用,随着负载的增加,它的性能下降是线性的。

对于扩展到大容量的性能,我们需要目前仅在扩展中才能找到的更高级的功能。例如,如果您希望在支持 Amp 的套接字服务器中同时为 10,000 个客户端提供服务,您应该使用基于 PHP 扩展的事件循环实现之一。但是,如果您在严格的本地程序中使用 Amp 以实现非阻塞并发,或者您不需要在服务器应用程序中同时处理数百个客户端,则原生 PHP 功能应该足够了。

全局访问器(Global Accessor)

Amp 对事件循环使用全局访问器,因为每个应用程序只有一个事件循环。同时运行两个循环是没有意义的,因为它们只需要以繁忙的等待方式相互调度才能正确运行。

应该通过 Amp\Loop提供的方法访问事件循环。首次使用访问器时,Amp 将自动设置可用的最佳驱动程序,请参阅下一节。

Amp\Loop::set()可用于设置自定义驱动程序或在测试中重置驱动程序,因为每个测试都应使用新的驱动程序实例运行以实现测试隔离。对于 PHPUnit,您可以使用TestListener 在每次测试后自动重置事件循环。

实现(Implementations)

Amp 基于各种后端提供不同的事件循环实现。所有实现都扩展了 Amp\Loop\Driver。从外部 API 的角度来看,它们的行为方式完全相同。主要区别与潜在的性能特征有关。当前的实现在这里列出:

ClassExtensionRepository
Amp\Loop\NativeDriver--
Amp\Loop\EvDriverpecl/evpecl-ev
Amp\Loop\EventDriverpecl/eventpecl-event
Amp\Loop\UvDriverpecl/uvpecl-uv

为您的应用程序选择哪一种实现并不重要。 Amp 将自动选择可用的最佳驱动程序。在本地依赖 NativeDriver进行开发的同时,在生产中拥有其中一个扩展是非常好的。

如果您想在开发过程中快速切换实现,例如为了比较或测试,您可以将 AMP_LOOP_DRIVER环境变量设置为其中一个类。如果您使用自定义实现,这仅在实现不带任何参数时才有效。

事件循环作为任务调度器(Event Loop as Task Scheduler)

为了有效地使用事件循环进行编程,我们首先需要了解的是:

The event loop is our task scheduler.

只要它运行,事件循环就控制程序流程。一旦我们告诉事件循环运行,它将保持控制,直到应用程序出错、无事可做或被明确停止。

思考这个非常简单的例子:

<?php

require "vendor/autoload.php";

use Amp\Loop;

function tick() {
    echo "tick\n";
}

echo "-- before Loop::run()\n";

Loop::run(function() {
    Loop::repeat($msInterval = 1000, "tick");
    Loop::delay($msDelay = 5000, "Amp\\Loop::stop");
});

echo "-- after Loop::run()\n";

执行上述示例后,您应该会看到如下输出:

-- before Loop::run()
tick
tick
tick
tick
-- after Loop::run()

这个输出表明事件循环的运行循环内部发生的事情就像它自己的独立程序一样。除非没有更多计划事件或调用 Loop::stop(),否则您的脚本将不会继续超过 Loop::run()点。

虽然应用程序可以并且经常确实完全在运行循环的范围内发生,但我们也可以使用事件循环来执行以下示例,该示例为交互式控制台输入强加了一个短暂的超时:

<?php

use Amp\Loop;

$myText = null;

function onInput($watcherId, $stream)
{
    global $myText;

    $myText = fgets($stream);
    stream_set_blocking(STDIN, true);

    Loop::cancel($watcherId);
    Loop::stop();
}

Loop::run(function () {
    echo "Please input some text: ";
    stream_set_blocking(STDIN, false);

    // Watch STDIN for input
    Loop::onReadable(STDIN, "onInput");

    // Impose a 5-second timeout if nothing is input
    Loop::delay($msDelay = 5000, "Amp\\Loop::stop");
});

var_dump($myText); // whatever you input on the CLI

// Continue doing regular synchronous things here.

显然,在这个例子中我们可以简单地同步使用 fgets(STDIN)。我们只是展示了可以根据需要进出事件循环以混合同步任务和非阻塞任务。

让我们继续学习事件循环 API

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值