Введение
Когда в программе работает несколько потоков, операционная система сама решает, какому из них и когда выделить процессорное время. Но мы можем подсказать ей, какие задачи важнее. Для этого у класса QThread есть приоритет выполнения. В статье разберём, какие бывают значения, как задать приоритет QThread при запуске и во время работы, и почему на разных системах результат может отличаться.
Что такое приоритет потока
Приоритет определяет, насколько часто планировщик ОС будет выбирать поток по сравнению с другими. Поток с высоким приоритетом получает процессор чаще и раньше, а с низким — только когда более важные потоки свободны. Приоритет не ускоряет сам код. Он лишь меняет очерёдность, когда несколько потоков одновременно претендуют на процессор.
Значения приоритета QThread
В Qt приоритеты описаны перечислением QThread::Priority. Каждому значению соответствует число от 0 до 7:
| Константа | Значение | Описание |
|---|---|---|
| QThread::IdlePriority | 0 | Выполняется, только когда другие потоки не заняты |
| QThread::LowestPriority | 1 | Планируется реже, чем LowPriority |
| QThread::LowPriority | 2 | Планируется реже, чем NormalPriority |
| QThread::NormalPriority | 3 | Обычный приоритет операционной системы |
| QThread::HighPriority | 4 | Планируется чаще, чем NormalPriority |
| QThread::HighestPriority | 5 | Планируется чаще, чем HighPriority |
| QThread::TimeCriticalPriority | 6 | Планируется как можно чаще |
| QThread::InheritPriority | 7 | Тот же приоритет, что у создающего потока. Значение по умолчанию |
Чем больше число (кроме InheritPriority), тем выше приоритет. По умолчанию новый поток наследует приоритет того потока, который его запустил.
Как задать приоритет QThread при запуске
Проще всего указать приоритет прямо в методе start():
Worker thread;
thread.start(QThread::HighPriority);
Так же он задаётся и при использовании схемы с moveToThread(), когда рабочий объект переносится в отдельный поток:
QThread *thread = new QThread;
Worker *worker = new Worker;
worker->moveToThread(thread);
connect(thread, &QThread::started, worker, &Worker::process);
connect(worker, &Worker::finished, thread, &QThread::quit);
connect(thread, &QThread::finished, worker, &QObject::deleteLater);
connect(thread, &QThread::finished, thread, &QObject::deleteLater);
thread->start(QThread::LowPriority);
Изменение приоритета во время работы
Метод setPriority() меняет приоритет уже запущенного потока:
Worker thread;
thread.start();
thread.setPriority(QThread::LowestPriority);
Важная деталь: если поток ещё не запущен, вызов setPriority() ничего не делает. Поэтому до старта приоритет нужно передавать в start(). Поток может изменить и свой собственный приоритет изнутри, например в методе run():
void Worker::run()
{
QThread::currentThread()->setPriority(QThread::IdlePriority);
// фоновая работа
}
Как узнать текущий приоритет
Текущее значение возвращает метод priority():
QThread::Priority p = thread.priority();
qDebug() << "Приоритет потока:" << p;
Если поток не запущен, метод вернёт QThread::InheritPriority.
Особенности разных операционных систем
Приоритет QThread — это только пожелание для планировщика. Как именно оно будет учтено, зависит от ОС:
- Windows сопоставляет значения Qt со своими уровнями приоритета потоков, и разница обычно заметна.
- Linux при стандартной политике планирования игнорирует приоритеты потоков. Документация Qt прямо указывает на это. Для реального эффекта нужны другие политики планирования и, как правило, права администратора.
- macOS учитывает приоритет, но также опирается на собственные механизмы качества обслуживания.
Поэтому не стоит строить логику программы на том, что поток с высоким приоритетом обязательно выполнится первым. Для синхронизации используйте мьютексы, условные переменные и сигналы.
Когда менять приоритет
- Понизить приоритет фоновых задач (индексация, сжатие, расчёт превью), чтобы интерфейс оставался отзывчивым.
- Повысить приоритет потока, который обрабатывает данные в реальном времени, например звук или поток с устройства.
- Избегать
TimeCriticalPriorityбез серьёзной причины. Такой поток может «отнять» процессор у всей системы.
Заключение
Приоритет QThread задаётся при запуске через start() или во время работы через setPriority(), а узнать его можно методом priority(). Помните, что приоритет лишь подсказывает планировщику, что важнее, и на разных ОС работает по-разному. Используйте его для разделения фоновых и срочных задач, но не для синхронизации потоков. Подробнее о самих потоках читайте в статье о QThread.