Введение в очереди задач в C++
Очередь задач (queue) — это одна из фундаментальных структур данных в программировании, которая работает по принципу FIFO (First In, First Out): первый добавленный элемент будет извлечён первым. Представьте себе обычную очередь в магазине — первый покупатель обслуживается первым. В C++ работа с очередями реализована через контейнер std::queue, который является частью Стандартной библиотеки шаблонов (STL).
В этой статье мы подробно разберём, как создавать, наполнять и обрабатывать очереди задач на C++. Вы узнаете, какие методы доступны, как избежать типичных ошибок и как очереди применяются в реальных проектах, от многопоточных систем до обработки запросов.
Основные операции с очередью задач
Создание и инициализация очереди
Чтобы использовать очередь в C++, необходимо подключить заголовочный файл <queue>. Базовая очередь объявляется так:
#include <iostream>
#include <queue>
int main() {
std::queue<int> taskQueue; // Очередь целых чисел
return 0;
}
Вы можете создать очередь для любого типа данных — строк, пользовательских структур, указателей. Например, очередь задач для обработки заказов:
#include <queue>
#include <string>
struct Order {
int id;
std::string description;
};
int main() {
std::queue<Order> orderQueue;
return 0;
}
Добавление и удаление элементов
Основные методы для работы с очередью:
push()— добавляет элемент в конец очередиpop()— удаляет первый элемент из очередиfront()— возвращает ссылку на первый элемент (без удаления)back()— возвращает ссылку на последний элементempty()— проверяет, пуста ли очередьsize()— возвращает количество элементов
Рассмотрим пример с пошаговой обработкой задач:
#include <iostream>
#include <queue>
int main() {
std::queue<std::string> tasks;
// Добавляем задачи
tasks.push("Задача 1: Проверить почту");
tasks.push("Задача 2: Написать отчёт");
tasks.push("Задача 3: Провести совещание");
std::cout << "Первая задача: " << tasks.front() << std::endl;
std::cout << "Последняя задача: " << tasks.back() << std::endl;
std::cout << "Всего задач: " << tasks.size() << std::endl;
// Обрабатываем задачи по очереди
while (!tasks.empty()) {
std::cout << "Выполняю: " << tasks.front() << std::endl;
tasks.pop(); // Удаляем выполненную задачу
}
std::cout << "Все задачи выполнены!" << std::endl;
return 0;
}
Результат выполнения:
Первая задача: Задача 1: Проверить почту
Последняя задача: Задача 3: Провести совещание
Всего задач: 3
Выполняю: Задача 1: Проверить почту
Выполняю: Задача 2: Написать отчёт
Выполняю: Задача 3: Провести совещание
Все задачи выполнены!
Приоритетные очереди: когда порядок важен
Иногда задачи имеют разный приоритет. Например, критическая ошибка должна быть обработана раньше планового обновления. В таких случаях используется std::priority_queue, которая автоматически сортирует элементы по приоритету.
#include <iostream>
#include <queue>
#include <vector>
int main() {
// По умолчанию — максимальный приоритет у наибольшего элемента
std::priority_queue<int> highPriorityTasks;
highPriorityTasks.push(5); // Обычная задача
highPriorityTasks.push(10); // Срочная задача
highPriorityTasks.push(1); // Низкий приоритет
while (!highPriorityTasks.empty()) {
std::cout << "Обрабатываю задачу с приоритетом: "
<< highPriorityTasks.top() << std::endl;
highPriorityTasks.pop();
}
return 0;
}
Вывод:
Обрабатываю задачу с приоритетом: 10
Обрабатываю задачу с приоритетом: 5
Обрабатываю задачу с приоритетом: 1
Чтобы изменить порядок на обратный (низкий приоритет — первым), используйте:
std::priority_queue<int, std::vector<int>, std::greater<int>> lowFirstQueue;
Реальные сценарии использования очередей
Обработка запросов в веб-сервере
Представьте, что ваш сервер получает множество HTTP-запросов. Очередь позволяет обрабатывать их по порядку, не теряя ни одного:
#include <iostream>
#include <queue>
#include <string>
struct Request {
int id;
std::string url;
};
class RequestHandler {
private:
std::queue<Request> pendingRequests;
public:
void addRequest(int id, const std::string& url) {
pendingRequests.push({id, url});
std::cout << "Запрос " << id << " добавлен в очередь\n";
}
void processAll() {
while (!pendingRequests.empty()) {
Request req = pendingRequests.front();
std::cout << "Обрабатываю запрос " << req.id
<< " к " << req.url << std::endl;
// Здесь может быть реальная обработка
pendingRequests.pop();
}
}
};
int main() {
RequestHandler handler;
handler.addRequest(1, "/api/users");
handler.addRequest(2, "/api/data");
handler.addRequest(3, "/api/login");
handler.processAll();
return 0;
}
Многопоточная очередь задач
В многопоточных приложениях очереди часто используются для передачи задач между потоками. Однако стандартная std::queue не является потокобезопасной. Для безопасной работы используйте мьютексы или готовые решения, например, tbb::concurrent_queue из библиотеки Intel TBB.