Процессы и потоки в Qt

Приоритет выполнения QThread: значения, start() и setPriority()

обновлено 1 034 просмотров

Введение

Когда в программе работает несколько потоков, операционная система сама решает, какому из них и когда выделить процессорное время. Но мы можем подсказать ей, какие задачи важнее. Для этого у класса QThread есть приоритет выполнения. В статье разберём, какие бывают значения, как задать приоритет QThread при запуске и во время работы, и почему на разных системах результат может отличаться.

Что такое приоритет потока

Приоритет определяет, насколько часто планировщик ОС будет выбирать поток по сравнению с другими. Поток с высоким приоритетом получает процессор чаще и раньше, а с низким — только когда более важные потоки свободны. Приоритет не ускоряет сам код. Он лишь меняет очерёдность, когда несколько потоков одновременно претендуют на процессор.

Значения приоритета QThread

В Qt приоритеты описаны перечислением QThread::Priority. Каждому значению соответствует число от 0 до 7:

КонстантаЗначениеОписание
QThread::IdlePriority0Выполняется, только когда другие потоки не заняты
QThread::LowestPriority1Планируется реже, чем LowPriority
QThread::LowPriority2Планируется реже, чем NormalPriority
QThread::NormalPriority3Обычный приоритет операционной системы
QThread::HighPriority4Планируется чаще, чем NormalPriority
QThread::HighestPriority5Планируется чаще, чем HighPriority
QThread::TimeCriticalPriority6Планируется как можно чаще
QThread::InheritPriority7Тот же приоритет, что у создающего потока. Значение по умолчанию
Приоритеты QThread

Чем больше число (кроме 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.