Zmienne warunkowe i wzorce pracowników
Koordynuj wątki za pomocą std::condition_variable i klasycznego wzorca puli pracowników
Zmienne warunkowe i wzorce pracowników to bezpłatna lekcja C++ Academy na CoddyKit. To lekcja 3 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 C++ Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs C++ Academy zawiera 4 lekcji w sumie.
Czym jest zmienna warunkowa?
Zmienna warunkowa pozwala wątkowi czekać do chwili, gdy inny wątek zasygnalizuje zmianę warunku. Służy do koordynowania producentów i konsumentów.
Podstawy std::condition_variable
Zmienna warunkowa współpracuje z std::mutex i predykatem. Oczekujący wątek zwalnia blokadę na czas oczekiwania i ponownie ją uzyskuje po wybudzeniu.
#include <condition_variable>
#include <mutex>
#include <queue>
std::mutex mtx;
std::condition_variable cv;
std::queue<int> tasks;
bool done = false;Oczekiwanie na warunek
Należy użyć cv.wait(lock, predicate). Funkcja atomowo odblokowuje mutex i usypia wątek do chwili otrzymania powiadomienia. Po wybudzeniu ponownie blokuje mutex i sprawdza predykat; jeśli jego wartość to false, wątek ponownie zasypia.
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [] { return !tasks.empty() || done; });Powiadamianie
Wątek producenta wywołuje cv.notify_one() lub cv.notify_all() po zmianie stanu. Podczas zmiany stanu należy posiadać blokadę.
{
std::lock_guard<std::mutex> lock(mtx);
tasks.push(42);
}
cv.notify_one();Wzorzec producent–konsument
Klasyczny przykład: producenci dodają zadania, a konsumenci czekają na zadania.
void consumer() {
while (true) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [] { return !tasks.empty() || done; });
if (done && tasks.empty()) break;
int task = tasks.front();
tasks.pop();
lock.unlock();
process(task);
}
}Pozorne wybudzenia
Wątek oczekujący na zmienną warunkową może zostać wybudzony bez powiadomienia. Dlatego wait przyjmuje predykat — sprawdza go ponownie po każdym wybudzeniu.
notify_one a notify_all
Należy używać notify_one, gdy tylko jeden oczekujący wątek może kontynuować pracę. Należy używać notify_all, gdy zmiana stanu dotyczy wszystkich oczekujących wątków, na przykład sygnału zamknięcia.
Oczekiwanie z limitem czasu
wait_for(lock, duration) i wait_until(lock, time_point) wybudzają wątek po powiadomieniu albo po upływie limitu czasu.
if (cv.wait_for(lock, 1s, [] { return ready; })) {
/* condition met */
} else {
/* timed out */
}Szkielet puli wątków roboczych
Typowy wzorzec: stały zestaw wątków roboczych pobiera zadania ze współdzielonej kolejki. Należy użyć mutexu, condition_variable, kolejki oraz atomowej flagi zatrzymania.
Unikanie zagubionych wybudzeń
Zawsze należy modyfikować współdzielony stan i wywoływać notify_*, posiadając mutex podczas zmiany stanu. Zwolnienie mutexu tuż przed powiadomieniem jest dozwolone, ale należy zachować właściwą kolejność. W przeciwnym razie oczekujący wątek może przeoczyć powiadomienie.
std::counting_semaphore (C++20)
W przypadku prostego sygnalizowania opartego na liczniku w C++20 dodano std::counting_semaphore — w niektórych wzorcach jest ono prostsze niż zmienne warunkowe.
Uwaga na zakleszczenie
Posiadanie wielu blokad podczas oczekiwania na zmienną warunkową może prowadzić do zakleszczenia. Należy pozyskiwać blokady w stałej kolejności i używać std::scoped_lock dla wielu mutexów.
Szybkie sprawdzenie
Dlaczego cv.wait(lock, predicate) przyjmuje predykat, nawet gdy chcą Państwo tylko czekać na powiadomienie?
Podsumowanie
Zmienna warunkowa koordynuje wątki oczekujące na zmianę stanu. Należy używać wait(lock, predicate), aby obsługiwać pozorne wybudzenia, modyfikować stan przy zablokowanym mutexie, a następnie wywoływać notify_one lub notify_all.
Często zadawane pytania
Czy lekcja „Zmienne warunkowe i wzorce pracowników” jest bezpłatna?
Tak — pełny tekst „Zmienne warunkowe i wzorce pracowników” 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 C++ Academy, przejdź na CoddyKit PRO. Kurs C++ Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Zmienne warunkowe i wzorce pracowników”?
Koordynuj wątki za pomocą std::condition_variable i klasycznego wzorca puli pracowników Ćwiczysz C++ 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ąć C++ Academy?
Nie wymagamy żadnego doświadczenia. C++ 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 3 z 4.
Ile czasu zajmuje lekcja „Zmienne warunkowe i wzorce pracowników”?
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 C++ Academy?
Tak. Każda lekcja C++ 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
- std::thread: łączenie i odłączanie
- std::mutex, lock_guard i unique_lock
- Zmienne warunkowe i wzorce pracowników
- Podstawy std::async i std::future