纤程

纤程概述

(PHP 8 >= 8.1.0)

纤程(Fiber)表示一组有完整栈、可中断的功能。 纤程可以在调用堆栈中的任何位置被挂起,在纤程内暂停执行,直到稍后恢复。

纤程可以暂停整个执行堆栈,所以该函数的直接调用者不需要改变调用这个函数的方式。

你可以在调用堆栈的任意地方使用 Fiber::suspend() 中断执行(也就是说,Fiber::suspend() 的调用位置可以在一个深度嵌套的函数中,甚至可以不存在)。

与无栈的 Generator 不同, 每一个 Fiber 拥有自己的调用栈,并允许在一个深度前度的函数调用中将它们暂停。 声明了中断(interruption)点的函数(即调用 Fiber::suspend()) 不需要改变自己的返回类型,不像使用 yield 一样需要返回一个 Generator 实例。

纤程可以在任意函数调用中被暂停,包括那些在 PHP VM 中被调用的函数。 例如被用于 array_map() 的函数或者提供 Iterator 实例以被 foreach 调用的方法。

纤程一旦被暂停,可以使用 Fiber::resume() 传递任意值、或者使用 Fiber::throw() 向纤程抛出一个异常以恢复运行。这个值或者异常将会在 Fiber::suspend() 中被返回(抛出)。

示例 #1 基础用法

<?php
$fiber 
= new Fiber(function (): void {
   
$value Fiber::suspend('fiber');
   echo 
"Value used to resume fiber: "$valuePHP_EOL;
});

$value $fiber->start();

echo 
"Value from fiber suspending: "$valuePHP_EOL;

$fiber->resume('test');
?>

以上例程会输出:

Value from fiber suspending: fiber
Value used to resume fiber: test
add a note add a note

User Contributed Notes 2 notes

up
3
user at csa dot es
23 days ago
Perhaps not using the same variable name everywhere will be a good idea

<?php
$fiber
= new Fiber(function (): void {
  
$parm = Fiber::suspend('fiber');
   echo
"Value used to resume fiber: ", $parm, PHP_EOL;
});

$res = $fiber->start();

echo
"Value from fiber suspending: ", $res, PHP_EOL;

$fiber->resume('test');
?>
up
0
maxpanchnko at gmail dot com
6 days ago
One of examples, how to make multi_curl faster twice (pseudocode) using Fibers:

<?php

$curlHandles
= [];
$urls = [
   
'https://example.com/1',
   
'https://example.com/2',
    ...
   
'https://example.com/1000',
];
$mh = curl_multi_init();
$mh_fiber = curl_multi_init();

$halfOfList = floor(count($urls) / 2);
foreach (
$urls as $index => $url) {
   
$ch = curl_init($url);
   
$curlHandles[] = $ch;

   
// half of urls will be run in background in fiber
   
$index > $halfOfList ? curl_multi_add_handle($mh_fiber, $ch) : curl_multi_add_handle($mh, $ch);
}

$fiber = new Fiber(function (CurlMultiHandle $mh) {
   
$still_running = null;
    do {
       
curl_multi_exec($mh, $still_running);
       
Fiber::suspend();
    } while (
$still_running);
});

// run curl multi exec in background while fiber is in suspend status
$fiber->start($mh_fiber);

$still_running = null;
do {
   
$status = curl_multi_exec($mh, $still_running);
} while (
$still_running);

do {
   
/**
     * at this moment curl in fiber already finished (maybe)
     * so we must refresh $still_running variable with one more cycle "do while" in fiber
     **/
   
$status_fiber = $fiber->resume();
} while (!
$fiber->isTerminated());

foreach (
$curlHandles as $index => $ch) {
   
$index > $halfOfList ? curl_multi_remove_handle($mh_fiber, $ch) : curl_multi_remove_handle($mh, $ch);
}
curl_multi_close($mh);
curl_multi_close($mh_fiber);
?>
To Top