0Pricing
C++ Academy · Урок

Условные переменные и шаблоны рабочих потоков

Координируйте потоки с помощью std::condition_variable и классического шаблона пула рабочих потоков.

«Условные переменные и шаблоны рабочих потоков» — бесплатный урок C++ Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C++ Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C++ Academy содержит 4 уроков всего.

Что такое переменная условия

Переменная условия позволяет потоку ждать, пока другой поток не сообщит об изменении условия. Она используется для координации производителей и потребителей.

Основы std::condition_variable

Переменная условия работает вместе с std::mutex и предикатом. Ожидающий поток освобождает блокировку во время ожидания и повторно захватывает её после пробуждения.

#include <condition_variable>
#include <mutex>
#include <queue>

std::mutex mtx;
std::condition_variable cv;
std::queue<int> tasks;
bool done = false;

Ожидание условия

Используйте cv.wait(lock, predicate). Этот вызов атомарно разблокирует мьютекс и приостанавливает поток до получения уведомления. После пробуждения он снова блокирует мьютекс и проверяет предикат; если результат ложен, поток снова переходит в состояние ожидания.

std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [] { return !tasks.empty() || done; });

Уведомление

Поток-производитель вызывает cv.notify_one() или cv.notify_all() после изменения состояния. Во время изменения состояния блокировка должна удерживаться.

{
    std::lock_guard<std::mutex> lock(mtx);
    tasks.push(42);
}
cv.notify_one();

Шаблон «производитель — потребитель»

Классический пример: производители добавляют задачи; потребители ожидают появления задач.

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

Ложные пробуждения

Поток, ожидающий на переменной условия, может проснуться без уведомления. Поэтому wait принимает предикат — он повторно проверяется после каждого пробуждения.

notify_one и notify_all

Используйте notify_one, когда продолжить работу может один ожидающий поток. Используйте notify_all, когда изменение состояния касается всех ожидающих потоков, например при отправке сигнала завершения.

Ожидание с ограничением времени

wait_for(lock, duration) и wait_until(lock, time_point) пробуждают поток либо после уведомления, либо по истечении времени ожидания.

if (cv.wait_for(lock, 1s, [] { return ready; })) {
    /* condition met */
} else {
    /* timed out */
}

Каркас пула рабочих потоков

Распространённый шаблон: фиксированный набор рабочих потоков получает задачи из общей очереди. Для реализации используйте мьютекс, переменную условия, очередь и атомарный флаг остановки.

Как избежать потерянных уведомлений

Всегда изменяйте общее состояние и вызывайте notify_*, удерживая мьютекс во время изменения состояния. Освободить его непосредственно перед уведомлением допустимо, но необходимо обеспечить правильный порядок операций. Иначе ожидающий поток может пропустить уведомление.

std::counting_semaphore (C++20)

Для простого сигнализирования на основе счётчика в C++20 добавлен std::counting_semaphore — в некоторых шаблонах он проще переменных условия.

Остерегайтесь взаимной блокировки

Удержание нескольких блокировок во время ожидания на переменной условия может привести к взаимной блокировке. Получайте блокировки в согласованном порядке и используйте std::scoped_lock для нескольких мьютексов.

Быстрая проверка

Почему cv.wait(lock, predicate) принимает предикат, даже если нужно лишь дождаться уведомления?

Итоги

Переменная условия координирует потоки, ожидающие изменения состояния. Используйте wait(lock, predicate) для обработки ложных пробуждений, изменяйте состояние под мьютексом, а затем вызывайте notify_one или notify_all.

Часто задаваемые вопросы

Урок «Условные переменные и шаблоны рабочих потоков» бесплатный?

Да — полный текст урока «Условные переменные и шаблоны рабочих потоков» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C++ Academy, подпишись на CoddyKit PRO. Курс C++ Academy содержит 4 уроков всего.

Чему я научусь в уроке «Условные переменные и шаблоны рабочих потоков»?

Координируйте потоки с помощью std::condition_variable и классического шаблона пула рабочих потоков. Ты практикуешь C++ Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать C++ Academy?

Предыдущий опыт не требуется. C++ Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «Условные переменные и шаблоны рабочих потоков»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке C++ Academy?

Да. Каждый урок C++ Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. std::thread: присоединение и отсоединение
  2. std::mutex, lock_guard и unique_lock
  3. Условные переменные и шаблоны рабочих потоков
  4. Основы std::async и std::future
← Назад к C++ Academy