Я хотел бы создать очень эффективную систему планировщика задач на С++.
Основная идея такова:
class Task {
public:
virtual void run() = 0;
};
class Scheduler {
public:
void add(Task &task, double delayToRun);
};
За Scheduler
должен существовать пул потоков фиксированного размера, который запускает задачи (я не хочу создавать поток для каждой задачи). delayToRun
означает, что task
не выполняется сразу, но delayToRun
секунд спустя (измерение с точки, в которую оно было добавлено в Scheduler
).
(delayToRun
означает значение "по крайней мере", конечно. Если система загружена или если мы спросим невозможное из Планировщика, она не сможет обрабатывать наш запрос. лучшее, что может)
И вот моя проблема. Как эффективно реализовать delayToRun
функциональность? Я пытаюсь решить эту проблему с использованием мьютексов и переменных условий.
Я вижу два пути:
С помощью менеджера потока
Планировщик содержит две очереди: allTasksQueue
и tasksReadyToRunQueue
. Задача добавляется в allTasksQueue
в Scheduler::add
. Существует менеджерский поток, который ждет наименьшее количество времени, поэтому он может поставить задачу от allTasksQueue
до tasksReadyToRunQueue
. Рабочие потоки ждут задачи, доступной в tasksReadyToRunQueue
.
Если Scheduler::add
добавляет задачу перед allTasksQueue
(задача, которая имеет значение delayToRun
, поэтому она должна идти до текущей задачи скорейшего запуска), тогда задача менеджера должна быть проснулся, чтобы он мог обновить время ожидания.
Этот метод можно считать неэффективным, так как ему нужны две очереди, и для выполнения задачи необходимо два файла condvar.signals(один для allTasksQueue
→ tasksReadyToRunQueue
, а один для сигнализации рабочего потока для фактического запуска задача)
Без потока менеджера
В планировщике есть одна очередь. Задача добавляется в эту очередь в Scheduler::add
. Рабочий поток проверяет очередь. Если он пуст, он ждет без ограничения по времени. Если он не пуст, он ждет самую быструю задачу.
-
Если есть только одна переменная условия, для которой ожидаются рабочие потоки: этот метод можно считать неэффективным, потому что если задача добавлена перед очередью (передняя часть означает, что есть N рабочих потоков, то индекс задачи < N), затем все рабочие потоки должны быть разбужены, чтобы обновить время, которое они ожидают.
-
Если для каждого потока есть отдельная переменная условия, тогда мы можем контролировать, какой поток просыпаться, поэтому в этом случае нам не нужно разбудить все потоки (нам нужно только просыпать поток который имеет наибольшее время ожидания, поэтому нам нужно управлять этим значением). Я сейчас думаю об этом, но разработка точных деталей сложна. Существуют ли какие-либо рекомендации/мысли/документы по этому методу?
Есть ли лучшее решение для этой проблемы? Я пытаюсь использовать стандартные возможности С++, но я готов использовать платформу (моя основная платформа - это linux), инструменты (например, pthreads) или даже специфические для Linux инструменты (например, futexes), если они обеспечивают лучшее решение.