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.00sZadania 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
- Model współbieżności PHP
- Fibers w PHP 8.1
- Pętle zdarzeń z ReactPHP
- Wydajne serwery z Swoole