0Pricing
PHP Academy · Lekcja

Model współbieżności PHP

Proszę zrozumieć blokujące operacje wejścia-wyjścia i dowiedzieć się, gdzie pomaga asynchroniczność.

Model współbieżności PHP to bezpłatna lekcja PHP Academy na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej PHP Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs PHP Academy zawiera 4 lekcji w sumie.

PHP jest domyślnie synchroniczny

Klasyczny PHP obsługuje jedno żądanie na proces, od góry do dołu, z blokującymi operacjami wejścia/wyjścia. Wywołanie file_get_contents() dla powolnego API zamraża cały skrypt, aż nadejdą dane. Model shared-nothing (nowy proces dla każdego żądania, a stan jest niszczony na końcu) jest niezwykle prosty — oznacza jednak, że pojedynczy proces roboczy może wykonywać tylko jedną rzecz naraz. Ta lekcja pokazuje, w jakich miejscach stanowi to problem i gdzie pomaga asynchroniczność.

Blokujące I/O w praktyce

Należy obserwować czas zegarowy zdominowany przez oczekiwanie, a nie obliczenia. Trzy sekwencyjne żądania trwające po 1 sekundzie zajmują około 3 sekund, mimo że procesor przez cały ten czas jest bezczynny.

<?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

Zadania zależne od CPU a zadania zależne od I/O

Współbieżność pomaga tylko w pracy ograniczonej przez I/O — czyli w czasie oczekiwania na sieć, dysk lub bazę danych. W przypadku pracy ograniczonej przez CPU (haszowanie, zmiana rozmiaru obrazów, parsowanie) pojedynczy wątek jest już w pełni obciążony; asynchroniczność tylko dodaje narzut związany z planowaniem. Najpierw należy ustalić przyczynę:

  • Wiele powolnych wywołań HTTP/zapytań do bazy danych → asynchroniczność daje dużą poprawę.
  • Intensywne pętle obliczeniowe → należy użyć przetwarzania równoległego (procesów/wątków), a nie pętli zdarzeń.

Jak dotąd skalowano PHP

Historycznie PHP skalowało się dzięki współbieżności na poziomie procesów: PHP-FPM utrzymuje pulę procesów roboczych; każdy obsługuje jedno żądanie naraz, a system operacyjny multipleksuje ich pracę. To rzeczywista współbieżność między żądaniami, ale nie daje niczego wewnątrz pojedynczego żądania — wciąż nie można równolegle rozproszyć 50 wywołań API w jednym handlerze, korzystając ze zwykłego blokującego kodu.

Idea pętli zdarzeń

Asynchroniczny PHP wprowadza pętlę zdarzeń: pojedynczy wątek rejestruje operacje wejścia-wyjścia jako nieblokujące, a następnie usypia w stream_select()/epoll do chwili, gdy któraś z nich będzie gotowa, wywołując callbacki w miarę napływania danych. Jeden wątek obsługuje tysiące gniazd, ponieważ nigdy nie pozostaje bezczynny, czekając na jedno z nich.

<?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
    }
}

Kooperatywność zamiast wywłaszczania

Asynchroniczny PHP działa kooperatywnie: zadania wykonują się, dopóki dobrowolnie nie oddadzą sterowania w punkcie I/O. Nie ma tu wywłaszczania, więc długie blokujące wywołanie lub ciasna pętla obliczeniowa zatrzymuje całą pętlę — pozostałe zadania zostają zagłodzone. Złota zasada: nigdy nie należy wywoływać funkcji blokujących (sleep, blokującego PDO, file_get_contents) wewnątrz zadania pętli zdarzeń. Należy używać ich asynchronicznych odpowiedników obsługiwanych przez pętlę.

Trzy podejścia w PHP

Nowoczesny PHP oferuje warstwowy zestaw narzędzi:

  • Fibers (PHP 8.1, rdzeń) — niskopoziomowy prymityw umożliwiający wstrzymywanie i wznawianie stosu wywołań. To element konstrukcyjny, a nie kompletny framework.
  • ReactPHP / Amp — pętle zdarzeń działające w kodzie użytkownika, oparte na strumieniach (a obecnie także na Fibers), zapewniające nieblokujące operacje wejścia-wyjścia bez rozszerzeń.
  • Swoole / OpenSwoole — rozszerzenie C udostępniające korutyny, wysokowydajny serwer i przechwytywanie blokujących wywołań.

Przed wyborem strategii należy sprawdzić, co jest dostępne w czasie działania.

<?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;

Promisy modelują przyszłe wartości

Zanim pojawiły się Fibers, asynchroniczny PHP udostępniał oczekujące wyniki jako promisy: zastępniki wartości, która będzie dostępna później. Callbacki dołącza się za pomocą then(). To działa, ale prowadzi do zagnieżdżonych łańcuchów; Fibers pozwalają bibliotekom ukryć ten mechanizm za synchronicznie wyglądającym await.

<?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";
});

Współbieżność a równoległość

Należy zachować precyzję:

  • Współbieżność — wiele zadań w toku, przeplatanych w jednym wątku (pętla zdarzeń). Świetnie sprawdza się w przypadku operacji wejścia-wyjścia.
  • Równoległość — wiele zadań wykonywanych jednocześnie na wielu rdzeniach (procesy, rozszerzenie parallel, pracownicy zadań Swoole). Jest potrzebna do pracy obciążającej CPU.

Pętla zdarzeń zapewnia współbieżność, a nie równoległość. Poniższy fragment pokazuje równoległość na poziomie procesów za pomocą proc_open — trzech pracowników działa jednocześnie w osobnych procesach systemu operacyjnego.

<?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";

Kiedy NIE sięgać po asynchroniczność

Asynchroniczność ma koszty: długo działający pracownik oznacza, że PHP nie usuwa już stanu między żądaniami, więc wycieki pamięci, statyczne cache i nieaktualność połączeń stają się problemem po stronie aplikacji. Jeśli obsługa żądania wykonuje jedno zapytanie do bazy danych i zwraca wynik, PHP-FPM jest prostszy i równie szybki. Po asynchroniczność warto sięgać w przypadku rozproszenia operacji wejścia-wyjścia, WebSocketów i długiego odpytywania, strumieniowania lub potrzeby użycia trwałego serwera.

Prawdziwa równoległość: ext-parallel i procesy

W przypadku rozdzielania pracy obciążającej CPU PHP może uruchamiać prawdziwą równoległość na poziomie systemu operacyjnego. Rozszerzenie parallel uruchamia wątki robocze z odizolowaną pamięcią; alternatywnie proc_open/pcntl_fork uruchamiają procesy. W przeciwieństwie do pętli zdarzeń mechanizmy te wykorzystują jednocześnie wiele rdzeni — są właściwym narzędziem, gdy wąskim gardłem są obliczenia, a nie oczekiwanie.

<?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;

Szybki test

Co się stanie po wywołaniu blokującego sleep(2) wewnątrz zadania pętli zdarzeń?

Podsumowanie

Teraz dostępny jest mentalny model współbieżności w PHP:

  • Domyślnie PHP działa synchronicznie i blokująco, w modelu bez współdzielenia stanu; FPM skaluje się między żądaniami, a nie wewnątrz jednego żądania.
  • Asynchroniczność pomaga w pracy zależnej od operacji wejścia-wyjścia dzięki jednowątkowej pętli zdarzeń korzystającej z nieblokujących strumieni.
  • Model jest kooperatywny — nie wolno blokować pętli; długa praca CPU wymaga równoległości, a nie współbieżności.
  • Zestaw narzędzi PHP obejmuje: Fibers (prymityw), ReactPHP/Amp (pętle działające w kodzie użytkownika i promisy), Swoole (rozszerzenie zapewniające korutyny).

Często zadawane pytania

Czy lekcja „Model współbieżności PHP” jest bezpłatna?

Tak — pełny tekst „Model współbieżności PHP” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu PHP Academy, przejdź na CoddyKit PRO. Kurs PHP Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Model współbieżności PHP”?

Proszę zrozumieć blokujące operacje wejścia-wyjścia i dowiedzieć się, gdzie pomaga asynchroniczność. Ćwiczysz PHP Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć PHP Academy?

Nie wymagamy żadnego doświadczenia. PHP Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.

Ile czasu zajmuje lekcja „Model współbieżności PHP”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji PHP Academy?

Tak. Każda lekcja PHP Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Model współbieżności PHP
  2. Fibers w PHP 8.1
  3. Pętle zdarzeń z ReactPHP
  4. Wydajne serwery z Swoole
← Powrót do PHP Academy