Revolt是并發(fā)PHP應(yīng)用程序的堅如磐石的事件循環(huán)。通常的PHP應(yīng)用程序?qū)⒋蟛糠謺r間花在等待I/O上。雖然PHP是單線程的,但可以使用協(xié)作多任務(wù)來允許并發(fā)性,方法是使用等待時間來做不同的事情。
PHP的傳統(tǒng)同步執(zhí)行流程很容易理解。一次只做一件事。如果查詢數(shù)據(jù)庫,則發(fā)送查詢并等待數(shù)據(jù)庫服務(wù)器的響應(yīng)。一旦你有了答案,你就可以開始做下一件事。
ReactPHP和其他庫已經(jīng)在PHP中提供了很長一段時間的協(xié)作多任務(wù)。然而,它們的事件驅(qū)動特性與許多現(xiàn)有的接口不兼容,需要不同的思維模型。PHP 8.1內(nèi)置了fibers,它提供了協(xié)作多線程。調(diào)用可以是異步的,沒有promise或回調(diào),同時仍然允許非阻塞I/O。
每個使用協(xié)同多任務(wù)的應(yīng)用程序都需要一個調(diào)度器(也稱為事件循環(huán)),這個包提供了這個調(diào)度器。Revolt是結(jié)合了React和ReactPHP的事件循環(huán)實(shí)現(xiàn)的多年經(jīng)驗(yàn)的結(jié)果。然而,它并不是一個用于編寫并發(fā)PHP應(yīng)用程序的成熟框架,而只是提供了必要的公共基礎(chǔ)。不同的(強(qiáng)烈的)固執(zhí)己見的庫可以在它的基礎(chǔ)上構(gòu)建,React和ReactPHP將繼續(xù)共存。
composer require revolt/event-loop
注意:此包可以作為Composer依賴項(xiàng)安裝在PHP 8.1及更高版本上。
<?phprequire __DIR__ . '/vendor/autoload.php';use Revolt/EventLoop;$suspension = EventLoop::getSuspension();$repeatId = EventLoop::repeat(1, function (): void { print '++ Executing callback created by EventLoop::repeat()' . PHP_EOL;});EventLoop::delay(5, function () use ($suspension, $repeatId): void { print '++ Executing callback created by EventLoop::delay()' . PHP_EOL; EventLoop::cancel($repeatId); $suspension->resume(null); print '++ Suspension::resume() is async!' . PHP_EOL;});print '++ Suspending to event loop...' . PHP_EOL;$suspension->suspend();print '++ Script end' . PHP_EOL;
在執(zhí)行上面的例子時,你應(yīng)該看到這樣的輸出:
++ Suspending to event loop...++ Executing callback created by EventLoop::repeat()++ Executing callback created by EventLoop::repeat()++ Executing callback created by EventLoop::repeat()++ Executing callback created by EventLoop::repeat()++ Executing callback created by EventLoop::delay()++ Suspension::resume() is async!++ Script end
這個輸出說明了事件循環(huán)內(nèi)部發(fā)生的事情就像它自己獨(dú)立的程序一樣。您的腳本將不會繼續(xù)通過 $suspension->suspend() 點(diǎn),除非掛起點(diǎn)通過 $suspension->resume() 或 $suspension->throw() 恢復(fù)。
雖然一個應(yīng)用程序可以而且經(jīng)常幾乎完全在事件循環(huán)的范圍內(nèi)發(fā)生,但我們也可以使用事件循環(huán)來做一些事情,比如下面的例子,它為交互式控制臺輸入施加了一個短暫的超時:
<?phprequire __DIR__ . '/vendor/autoload.php';use Revolt/EventLoop;if (/stream_set_blocking(STDIN, false) !== true) { /fwrite(STDERR, "Unable to set STDIN to non-blocking" . PHP_EOL); exit(1);}print "Write something and hit enter" . PHP_EOL;$suspension = EventLoop::getSuspension();$readableId = EventLoop::onReadable(STDIN, function ($id, $stream) use ($suspension): void { EventLoop::cancel($id); $chunk = /fread($stream, 8192); print "Read " . /strlen($chunk) . " bytes" . PHP_EOL; $suspension->resume(null);});$timeoutId = EventLoop::delay(5, function () use ($readableId, $suspension) { EventLoop::cancel($readableId); print "Timeout reached" . PHP_EOL; $suspension->resume(null);});$suspension->suspend();EventLoop::cancel($readableId);EventLoop::cancel($timeoutId);
顯然,我們可以在這個例子中簡單地同步使用 fgets(STDIN) 。我們只是在演示可以根據(jù)需要進(jìn)出事件循環(huán),以混合同步任務(wù)和非阻塞任務(wù)。
事件循環(huán)公開了幾種調(diào)度計時器的方法。
Deferred 回調(diào)
案例
<?php/** * @author Tinywan(ShaoBo Wan) * @email 756684177@qq.com * @date 2024/1/31 18:24 */require 'vendor/autoload.php';use Revolt/EventLoop;echo "line 1/n";EventLoop::defer(function (): void { echo "line 3/n";});echo "line 2/n";EventLoop::run();
輸出
line 1line 2line 3
Delayed 回調(diào)
案例
<?php/** * @author Tinywan(ShaoBo Wan) * @email 756684177@qq.com * @date 2024/1/31 18:24 */require 'vendor/autoload.php';use Revolt/EventLoop;EventLoop::delay(3, function (): void { print '3 seconds passed';});EventLoop::run();
3秒后輸出
3 seconds passed
Periodic 定期回調(diào)
案例
<?php/** * @author Tinywan(ShaoBo Wan) * @email 756684177@qq.com * @date 2024/1/31 18:49 */require 'vendor/autoload.php';use Revolt/EventLoop;EventLoop::repeat(0.1, function ($callbackId): void { static $i = 0; if ($i++ < 3) { echo "tick/n"; } else { EventLoop::cancel($callbackId); }});EventLoop::run();
輸出
tickticktick
定時器偏差
重復(fù)計時器基本上是簡單的延遲計時器,在觸發(fā)適當(dāng)?shù)奶幚沓绦蛑皶詣又匦抡{(diào)度。它們受定時器漂移的影響。多個計時器可能會堆疊在一起,以防它們作為協(xié)程執(zhí)行。
Revolt被設(shè)計為可以很好地與纖維一起工作。所有事件回調(diào)都在單獨(dú)的纖程中運(yùn)行,并且可以隨時掛起它。如果在事件回調(diào)中沒有掛起,則纖程將被重用于將來的事件回調(diào)以保存資源。
掛起允許通過掛起當(dāng)前執(zhí)行上下文來等待事件,直到所討論的事件發(fā)生。它們將掛起當(dāng)前纖程并返回到事件循環(huán),或者如果從纖程外部(即從 {main} )調(diào)用,則開始運(yùn)行事件循環(huán)。
應(yīng)使用 Revolt/EventLoop/Suspension API暫停和恢復(fù)光纖。Suspension 對象可以使用 Revolt/EventLoop::getSuspension() 創(chuàng)建。在獲得 Suspension 對象之后,可以注冊事件回調(diào)以調(diào)度當(dāng)前纖程的恢復(fù)。$suspension->suspend() 將掛起當(dāng)前的執(zhí)行上下文,直到它通過 $suspension->resume() 或 $suspension->throw()恢復(fù)。
案例:讓我們暫停主執(zhí)行上下文,直到有數(shù)據(jù)從 STDIN 讀取或超時到期:
<?phprequire __DIR__ . '/vendor/autoload.php';use Revolt/EventLoop;if (/stream_set_blocking(STDIN, false) !== true) { /fwrite(STDERR, "Unable to set STDIN to non-blocking" . PHP_EOL); exit(1);}print "Write something and hit enter" . PHP_EOL;$suspension = EventLoop::getSuspension();$readableId = EventLoop::onReadable(STDIN, function ($id, $stream) use ($suspension): void { EventLoop::cancel($id); $chunk = /fread($stream, 8192); print "Read " . /strlen($chunk) . " bytes" . PHP_EOL; $suspension->resume(null);});$timeoutId = EventLoop::delay(5, function () use ($readableId, $suspension) { EventLoop::cancel($readableId); print "Timeout reached" . PHP_EOL; $suspension->resume(null);});$suspension->suspend();EventLoop::cancel($readableId);EventLoop::cancel($timeoutId);
自動超時輸出
Write something and hit enterTimeout reached
按Enter鍵盤輸出
Write something and hit enterRead 1 bytes
信號是類Unix操作系統(tǒng)中的標(biāo)準(zhǔn)化消息。
EventLoop::onSignal() 可用于對發(fā)送到進(jìn)程的信號作出反應(yīng)。
<?phprequire __DIR__ . '/vendor/autoload.php';use Revolt/EventLoop;// Let's tick off output once per second, so we can see activity.EventLoop::repeat(1, function (): void { echo "tick: ", date('c'), "/n";});// What to do when a SIGINT signal is receivedEventLoop::onSignal(SIGINT, function (): void { echo "Caught SIGINT! exiting .../n"; exit;});EventLoop::run();
SIGINT 信號: 當(dāng)用戶按某些終端鍵時, 引發(fā)終端產(chǎn)生的信號. 如Ctrl+C鍵, 這將產(chǎn)生中斷信號SIGINT. 它將停止一個已失去控制的程序。
Ctrl+C 輸出
tick: 2024-01-31T11:54:03+00:00tick: 2024-01-31T11:54:04+00:00tick: 2024-01-31T11:54:05+00:00tick: 2024-01-31T11:54:06+00:00tick: 2024-01-31T11:54:07+00:00tick: 2024-01-31T11:54:08+00:00tick: 2024-01-31T11:54:09+00:00tick: 2024-01-31T11:54:10+00:00tick: 2024-01-31T11:54:11+00:00tick: 2024-01-31T11:54:12+00:00^CCaught SIGINT! exiting ...
從基本原理中可以清楚地看到,信號回調(diào)可以像任何其他事件回調(diào)一樣被啟用、禁用和取消。一般來說,如果所有回調(diào)都消失了,只有信號回調(diào)仍然存在,那么您希望退出事件循環(huán),除非您沒有主動等待該事件發(fā)生。
ext-uv 暴露 UV::SIG* 常量用于可觀察信號。使用 EventDriver 的應(yīng)用程序在注冊信號回調(diào)或依賴 ext-pcntl 時需要手動指定適當(dāng)?shù)恼麛?shù)信號編號。
本文鏈接:http://www.www897cc.com/showinfo-26-70393-0.htmlPHP 高性能的事件循環(huán)庫 Revolt
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。郵件:2376512515@qq.com