Зачем выносить работу из интерфейсного потока
Если слот кнопки выполняет долгую операцию прямо в GUI-потоке, окно перестаёт перерисовываться и не реагирует на действия пользователя. Для независимой вычислительной задачи в Qt удобно использовать QtConcurrent::run: функция выполняется в пуле потоков, а результат можно получить через QFuture и отследить с помощью QFutureWatcher.
Подход подходит, например, для разбора большого файла, вычисления статистики или обработки набора изображений. Он не заменяет очереди сигналов и слотов для постоянно работающего рабочего объекта: задача в QtConcurrent::run должна завершиться и вернуть результат.
Минимальный пример
Подключите модуль Concurrent и создайте наблюдатель как поле окна, чтобы он жил до завершения задачи:
#include <QtConcurrent/QtConcurrentRun>
#include <QFutureWatcher>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
private:
QFutureWatcher<QString> m_watcher;
};
Запустите функцию и подключите обработчик сигнала finished:
connect(&m_watcher, &QFutureWatcher<QString>::finished,
this, [this] {
const QString result = m_watcher.result();
ui->resultLabel->setText(result);
ui->startButton->setEnabled(true);
});
ui->startButton->setEnabled(false);
m_watcher.setFuture(QtConcurrent::run([] {
// Здесь выполняется работа без доступа к виджетам.
return QStringLiteral("Готово");
}));
Сигнал наблюдателя доставляется объекту в его потоке, поэтому обновлять интерфейс в обработчике можно безопасно, если сам MainWindow жив. Рабочая функция при этом не должна обращаться к виджетам: GUI-классы Qt предназначены для использования из главного потока.
Ошибки и отмена
Если задача может завершиться исключением, обработайте его при чтении результата: вызов result() передаст исключение вызывающему коду. Не оставляйте такое чтение без обработки, если ошибка должна быть показана пользователю. Для отмены сначала проверьте, поддерживает ли конкретный тип задачи отмену. Обычная функция, запущенная через QtConcurrent::run, не прерывается автоматически посреди произвольного кода только от вызова cancel().
Что важно помнить
- Не блокируйте GUI-поток ожиданием через
waitForFinished()— иначе теряется смысл фонового выполнения. - Передавайте рабочей функции данные по значению или обеспечьте им срок жизни до окончания задачи.
- Не изменяйте один и тот же объект из нескольких потоков без синхронизации.
- Отключайте повторный запуск, если параллельные копии операции могут повредить состояние приложения.
QtConcurrent::run вместе с QFutureWatcher даёт компактный способ выполнить ограниченную по времени вычислительную работу и вернуть результат в интерфейс. Для долгоживущего рабочего процесса, управления очередью или тонкого контроля отмены лучше выбрать отдельный объект рабочего потока с явным протоколом сигналов и слотов.