PHP.mk документација

Влакна

Почист и полокален преглед на PHP референцата, со задржана структура од PHP.net и подобра читливост за примери, секции и белешки.

language.fibers.php PHP.net прокси Преводот се освежува
Оригинал на PHP.net
Патека language.fibers.php Локална патека за оваа страница.
Извор php.net/manual/en Оригиналниот HTML се реупотребува и локално се стилизира.
Режим Прокси + превод во позадина Кодовите, табелите и белешките остануваат читливи во истиот тек.
Влакна

Референца за `language.fibers.php` со подобрена типографија и навигација.

language.fibers.php

Влакна

Преглед на влакна

(PHP 8 >= 8.1.0)

Влакната претставуваат целосни, прекинали функции. Влакната може да бидат суспендирани од било каде во стек од повици, паузирајќи ја извршувањето во рамките на влакното додека влакното не се продолжи во подоцнежен момент.

Влакната ја паузираат целата стек од извршување, така што директниот повикувач на функцијата не треба да го менува начинот на кој ја повикува функцијата.

Извршувањето може да биде прекинато било каде во стек од повици користејќи Го продолжува влакното со фрлање на дадениот исклучок од тековниот (односно, повикот до Го продолжува влакното со фрлање на дадениот исклучок од тековниот може да биде во длабоко вгнездена функција или воопшто да не постои).

За разлика од безстек за собирање на сите вредности додека ги игнорира клучевите вратени ода, секое Влакно има свој стек од повици, дозволувајќи им да бидат паузирани во длабоко вгнездени повици на функции. Функцијата што декларира точка на прекин (односно, повикувајќи Го продолжува влакното со фрлање на дадениот исклучок од тековниот) не треба да го менува својот тип на враќање, за разлика од функцијата што користи yield што мора да врати за собирање на сите вредности додека ги игнорира клучевите вратени од instance.

Влакната може да бидат суспендирани во било кој повик на функција, вклучувајќи ги и оние повикани од внатрешноста на PHP VM, како што се функциите обезбедени за array_map() или методи повикани од foreach на Итератор object.

Откако ќе се суспендира, извршувањето на влакното може да се продолжи со било која вредност користејќи Fiber::start() или со фрлање исклучок во влакното користејќи Fiber::resume(). Вредноста се враќа (или се фрла исклучок) од Го продолжува влакното со фрлање на дадениот исклучок од тековниот.

Забелешка: Пред PHP 8.4.0, префрлањето на влакна за време на извршувањето на објект destructor не беше дозволено.

Пример #1 Основна употреба

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

$value = $fiber->start();

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

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

Пример #1 Пример што покажува затворачка ознака што го опфаќа последниот нов ред

Value from fiber suspending: fiber
Value used to resume fiber: test

Белешки од корисници 6 белешки

корисник на csa dot es
3 години пред
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');
?>
Али Мадади
3 години пред
Here is a simple scheduler and thread pool that implements multithreading using fibers and tick functions in PHP 8.1 and returns the return value of each function in the pool in an array at the end.

Note that due to some bugs, you need to register a new tick function for each "thread". Remember to unregister all of them at the end.

The link bellow is the discussion on a bug that is going on right now (At the time of writing this). Note that based on the discussion, the ability to call Fiber::suspend() inside tick function may become forbidden in PHP 8.2+. But if the bug gets fixed, you can move register_tick_function() line to the top of the class, and this simple multithreading class in pure PHP code will work like a charm.
https://github.com/php/php-src/issues/8960

<?php

declare(ticks=1);

class Thread {
  protected static $names = [];
  protected static $fibers = [];
  protected static $params = [];

  public static function register(string|int $name, callable $callback, array $params)
  {
    self::$names[]  = $name;
    self::$fibers[] = new Fiber($callback);
    self::$params[] = $params;
  }

  public static function run() {
    $output = [];

    while (self::$fibers) {
      foreach (self::$fibers as $i => $fiber) {
          try {
              if (!$fiber->isStarted()) {
                  // Register a new tick function for scheduling this fiber
                  register_tick_function('Thread::scheduler');
                  $fiber->start(...self::$params[$i]);
              } elseif ($fiber->isTerminated()) {
                  $output[self::$names[$i]] = $fiber->getReturn();
                  unset(self::$fibers[$i]);
              } elseif ($fiber->isSuspended()) {
                $fiber->resume();
              }                
          } catch (Throwable $e) {
              $output[self::$names[$i]] = $e;
          }
      }
    }

    return $output;
  }

  public static function scheduler () {
    if(Fiber::getCurrent() === null) {
      return;
    }

    // running Fiber::suspend() in this if condition will prevent an infinite loop!
    if(count(self::$fibers) > 1)
    {
      Fiber::suspend();
    }
  }
}

?>

And here is an example code on how to use above Thread class:

<?php

// defining a non-blocking thread, so multiple calls will run in concurrent mode using above Thread class.
function thread (string $print, int $loop)
{
  $i = $loop;
  while ($i--){
    echo $print;
  }

  return "Thread '{$print}' finished after printing '{$print}' for {$loop} times!";
}

// registering 6 Threads (A, B, C, D, E, and F)
foreach(range('A', 'F') as $c) {
  Thread::register($c, 'thread', [$c, rand(5, 20)]);
}

// run threads and wait until execution finishes
$outputs = Thread::run();

// print outputs
echo PHP_EOL, '-------------- RETURN VALUES --------------', PHP_EOL;
print_r($outputs);

?>

The output will be something like this (but probably different):

ABCDEFABCDEFABCDEFABCDEFABCDEFABCEFABFABFABEBEFBEFEFEFAABEABEBEFBEFFAAAAAA
-------------- RETURN VALUES --------------
Array
(
    [D] => Thread 'D' finished after printing 'D' for 5 times!
    [C] => Thread 'C' finished after printing 'C' for 6 times!
    [E] => Thread 'E' finished after printing 'E' for 15 times!
    [B] => Thread 'B' finished after printing 'B' for 15 times!
    [F] => Thread 'F' finished after printing 'F' for 15 times!
    [A] => Thread 'A' finished after printing 'A' for 18 times!
)
nikiDOTamministratoreATgmail на no dot spam
пред 1 година
TL;DR

The Thread class from Ali Madabi above has been eventually deprecated by the linked issue as relaying on tick functions for preemptive multi-threading simulation has been deemed "bad practice". Better ways were suggested for achieving some sort of multi-threading, such as: Revolt and AMP.

https://github.com/php/php-src/issues/8960#issuecomment-1184249445
nesk на xakep dot ru
3 години пред
I think that in some cases it makes sense to convert a Fiber to a Generator (Coroutine) for convenience. In such cases, this code will be useful:

<?php
function fiber_to_coroutine(\Fiber $fiber): \Generator
{
    $index = -1; // Note: Pre-increment is faster than post-increment.
    $value = null;

    // Allow an already running fiber.
    if (!$fiber->isStarted()) {
        $value = yield ++$index => $fiber->start();
    }

    // A Fiber without suspends should return the result immediately.
    if (!$fiber->isTerminated()) {
        while (true) {
            $value = $fiber->resume($value);

            // The last call to "resume()" moves the execution of the
            // Fiber to the "return" stmt.
            //
            // So the "yield" is not needed. Skip this step and return
            // the result.
            if ($fiber->isTerminated()) {
                break;
            }

            $value = yield ++$index => $value;
        }
    }

    return $fiber->getReturn();
}
?>
maxpanchnko на gmail dot com
3 години пред
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);
?>
newuser
3 години пред
Example of the same functionality showing what is the difference between Fiber and Generator
<?php
$gener = (function () use (&$gener): Generator {
    $userfunc = function () use (&$gener) : Generator {
        register_shutdown_function(function () use (&$gener) {
            $gener->send('test');
        });
        return yield 'test';
    };
    $parm = yield from $userfunc();
    echo "Value used to resume fiber: ", $parm, PHP_EOL;
})();

$res = $gener->current();
echo "Value from fiber suspending: ", $res, PHP_EOL;
?>
<?php
$fiber = new Fiber(function () use (&$fiber) : void {
    $userfunc = function () use (&$fiber) : string {
        register_shutdown_function(function () use (&$fiber) {
            $fiber->resume('test');
        });
        return Fiber::suspend('fiber');
    };
    $parm = $userfunc(); 
    echo "Value used to resume fiber: ", $parm, PHP_EOL;
});

$res = $fiber->start();
echo "Value from fiber suspending: ", $res, PHP_EOL;
?>
На оваа страница

Автоматски outline од активната документација.

Насловите ќе се појават тука по вчитување.

Попрегледно читање

Примерите, changelog табелите и user notes се визуелно издвоени за да не се губат во долгата содржина.

Брз совет Користи го outline-от Скокни директно на главните секции од активната страница.
Извор Оригиналниот линк останува достапен Кога ти треба целосен upstream context, отвори го PHP.net во нов tab.