PHP Academy · Lektion

PHP's concurrency-model

Forstå blocking I/O, og hvor async hjælper.

Lektion 1 af 413 trin

PHP's concurrency-model er en gratis PHP Academy-lektion på CoddyKit. Dette er lektion 1 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i PHP Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. PHP Academy-kurset indeholder 4 lektioner i alt.

PHP er som standard synkront

Klassisk PHP kører én anmodning pr. proces, fra top til bund, med blokerende I/O. Et file_get_contents()-kald til en langsom API fryser hele scriptet, indtil bytesene ankommer. Modellen uden delt tilstand (en ny proces for hver anmodning, hvor tilstanden nedlægges til sidst) er vidunderligt enkel — men det betyder, at én arbejdsproces kun kan gøre én ting ad gangen. Denne lektion viser, hvor det gør ondt, og hvor asynkron behandling hjælper.

Mål den blokerende I/O

Se, hvordan den faktiske tid domineres af ventetid, ikke beregning. Tre sekventielle anmodninger på ét sekund tager cirka 3 sekunder, selv om CPU'en er inaktiv hele tiden.

<?php
$start = microtime(true);
foreach (['a', 'b', 'c'] as $job) {
    usleep(1_000_000); // simulate a 1s blocking network call
    echo "done $job\n";
}
printf("elapsed: %.2fs\n", microtime(true) - $start); // ~3.00s

CPU-begrænset kontra I/O-begrænset

Samtidighed hjælper kun med I/O-begrænset arbejde — tid, der bruges på at vente på netværket, disken eller databasen. Ved CPU-begrænset arbejde (hashberegning, ændring af billedstørrelse, fortolkning) er en enkelt tråd allerede mættet; asynkron behandling tilføjer kun planlægningsomkostninger. Analysér først:

  • Mange langsomme HTTP-kald eller databaseforespørgsler → asynkron behandling giver stor gevinst.
  • Tætte numeriske løkker → brug parallelisering (processer/tråde), ikke en hændelsesløkke.

Sådan har PHP skaleret indtil nu

Historisk har PHP skaleret med samtidighed på procesniveau: PHP-FPM holder en pulje af arbejdsprocesser, som hver håndterer én anmodning ad gangen, mens operativsystemet multiplekserer dem. Det er reel samtidighed på tværs af anmodninger, men giver intet inden i en anmodning — du kan stadig ikke sende 50 API-kald parallelt inde i én handler med almindelig blokerende kode.

Idéen bag event-loopet

Asynkron PHP introducerer et event-loop: en enkelt tråd, der registrerer I/O-operationer som ikke-blokerende og derefter sover i stream_select()/epoll, indtil en af dem er klar, hvorefter callbacks kaldes, når data ankommer. Én tråd håndterer tusindvis af sockets, fordi den aldrig sidder inaktiv og venter på én bestemt socket.

<?php
// Conceptual loop: poll many non-blocking streams at once
$streams = openManyNonBlockingSockets();
while ($streams) {
    $read = $streams; $write = $except = [];
    stream_select($read, $write, $except, null); // sleeps until ANY is ready
    foreach ($read as $s) {
        handleReadyStream($s); // runs only the sockets that have data now
    }
}

Kooperativ, ikke præemptiv

Asynkron PHP er kooperativ: Opgaver kører, indtil de frivilligt giver kontrollen fra sig ved et I/O-punkt. Der er ingen præemption, så et langt blokerende kald eller en tæt CPU-løkke standser hele loopet — alle andre opgaver sulter. Den gyldne regel er: Kald aldrig blokerende funktioner (sleep, blokerende PDO, file_get_contents) i en opgave, der kører i event-loopet. Brug loopets asynkrone alternativer.

Tre tilgange i PHP

Moderne PHP tilbyder et lagdelt værktøjssæt:

  • Fibers (PHP 8.1, kerne) — en lavniveauprimitiv til at sætte en kaldestak på pause og genoptage den. Byggestenen, ikke et komplet framework.
  • ReactPHP / Amp — event-loops i brugerkode, bygget på streams (og nu Fibers), til ikke-blokerende I/O uden udvidelser.
  • Swoole / OpenSwoole — en C-udvidelse, der leverer coroutines, en højtydende server og understøttelse af blokerende kald.

Undersøg, hvad der er tilgængeligt ved kørsel, før du vælger en strategi.

<?php
echo 'Fibers: ' . (class_exists('Fiber') ? 'yes' : 'no'), PHP_EOL;
echo 'Swoole ext: ' . (extension_loaded('swoole') ? 'yes' : 'no'), PHP_EOL;
echo 'parallel ext: ' . (extension_loaded('parallel') ? 'yes' : 'no'), PHP_EOL;
echo 'PHP ' . PHP_VERSION, PHP_EOL;

Promises modellerer fremtidige værdier

Før Fibers viste asynkron PHP ventende resultater som promises: en pladsholder for en værdi, der bliver klar senere. Du tilknytter callbacks via then(). Det fungerer, men fører til indlejrede kæder; Fibers lader biblioteker skjule dette bag et await, der ser synkront ud.

<?php
use React\Promise\Promise;

$promise = new Promise(function ($resolve) {
    // resolved later when I/O completes
    $resolve(42);
});
$promise->then(function ($value) {
    echo "got $value\n";
});

Samtidighed kontra parallelitet

Vær præcis:

  • Samtidighed — mange opgaver er i gang og flettes ind mellem hinanden på én tråd (event-loop). Fantastisk til I/O.
  • Parallelitet — mange opgaver udføres samtidigt på flere kerner (processer, parallel-udvidelsen, Swoole-opgavearbejdere). Nødvendigt til CPU-arbejde.

Et event-loop giver samtidighed, ikke parallelitet. Kodestumpen nedenfor viser parallelitet på procesniveau med proc_open — tre arbejdere kører samtidig i hver sin operativsystemproces.

<?php
$procs = [];
foreach (range(1, 3) as $i) {
    // Each worker is a separate OS process -> real parallelism
    $procs[] = proc_open(
        "php -r 'usleep(500000); echo \"worker $i done\\n\";'",
        [1 => ['pipe', 'w']], $pipes
    );
}
foreach ($procs as $p) { proc_close($p); }
echo "all workers launched in parallel\n";

Hvornår du IKKE skal vælge asynkron kode

Asynkron kode har omkostninger: En langvarigt kørende arbejder betyder, at PHP ikke længere rydder tilstanden mellem forespørgsler, så hukommelseslækager, statiske caches og forældede forbindelser bliver dit problem. Hvis en forespørgselsbehandler udfører én databaseforespørgsel og returnerer, er PHP-FPM enklere og lige så hurtigt. Vælg asynkron kode, når du har brug for I/O-forgrening, WebSockets/langvarig polling, streaming eller en vedvarende server.

Ægte parallelitet: ext-parallel og processer

Ved CPU-begrænset forgrening kan PHP køre ægte parallelitet på operativsystemniveau. Udvidelsen parallel starter arbejdstråde med isoleret hukommelse; alternativt opretter proc_open/pcntl_fork processer. I modsætning til et event-loop bruger disse flere kerner samtidigt — det rigtige værktøj, når flaskehalsen er beregning og ikke ventetid.

<?php
// Requires ext-parallel — runs tasks on separate threads/cores
use parallel\Runtime;

$runtime = new Runtime();
$future = $runtime->run(function (): int {
    $sum = 0;
    for ($i = 0; $i < 1_000_000; $i++) $sum += $i; // CPU work on another core
    return $sum;
});
echo $future->value(), PHP_EOL;

Hurtigt tjek

Hvad sker der, hvis du kalder blokerende sleep(2) i en opgave, der kører i event-loopet?

Opsamling

Du har nu en mental model for samtidighed i PHP:

  • PHP er som standard synkron, blokerende og uden delt tilstand; FPM skalerer på tværs af forespørgsler, ikke inden for én forespørgsel.
  • Asynkron kode hjælper med I/O-begrænset arbejde via et enkelttrådet event-loop, der bruger ikke-blokerende streams.
  • Modellen er kooperativ — blokér aldrig loopet; langt CPU-arbejde kræver parallelitet, ikke samtidighed.
  • PHP's værktøjssæt: Fibers (primitiv), ReactPHP/Amp (event-loops i brugerkode + promises), Swoole (coroutine-udvidelse).
Gratis at komme i gang

Lær PHP med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
49
Lektioner
195

Ofte stillede spørgsmål

Er lektionen “PHP's concurrency-model” gratis?

Ja — hele teksten til “PHP's concurrency-model” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af PHP Academy-kurset, skal du opgradere til CoddyKit PRO. PHP Academy-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “PHP's concurrency-model”?

Forstå blocking I/O, og hvor async hjælper. Du øver dig i PHP Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på PHP Academy?

Der kræves ingen tidligere erfaring. PHP Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 1 af 4.

Hvor lang tid tager lektionen “PHP's concurrency-model”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne PHP Academy-lektion?

Ja. Alle PHP Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. PHP's concurrency-model
  2. PHP 8.1 Fibers
  3. Event loops med ReactPHP
  4. Højtydende servere med Swoole
← Tilbage til PHP Academy