Criando Threads com C++ Puro
Para entender o funcionamento de threads, é essencial compreender como a execução concorrente pode ser implementada em C++. Um exemplo básico envolve criar um thread que exibe valores incrementais em uma interface gráfica. No entanto, operações bloqueantes, como sleep(), executadas no thread principal causam travamentos na interface.
void Widget::on_pushButton_clicked() {
int contador = 0;
while (true) {
ui->lcdNumber->display(QString::number(contador++));
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
Esse código, quando executado diretamente no slot do botão, bloqueia toda a interface, tornando-a inacessível. A solução é mover essa lógica para um thread separado.
Uso de std::thread com detach()
Utilizando std::thread, podemos iniciar uma nova thread que roda em paralelo:
void Widget::on_pushButton_clicked() {
std::thread worker(&Widget::updateCounter, this);
worker.detach(); // Libera o controle da thread
}
void Widget::updateCounter() {
int valor = 0;
while (true) {
qDebug() << "Contador: " << valor;
ui->lcdNumber->display(QString::number(valor++));
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
O uso de detach() permite que o thread execute em segundo plano sem bloquear o thread principal. Como o objeto std::thread é local, ele será automaticamente destruído ao final da função, evitando vazamentos de recursos.
Implementação com QThread
Apesar de funcional, o uso direto de std::thread não é ideal em projetos Qt por falta de integração com o sistema de eventos. A melhor prática é usar QThread.
Crie uma classe derivada de QThread e sobrescreva o método run():
class WorkerThread : public QThread {
Q_OBJECT
public:
explicit WorkerThread(QObject *parent = nullptr) : QThread(parent) {}
protected:
void run() override {
int valor = 0;
while (valor <= 10) {
emit progressUpdated(valor++);
msleep(1000);
}
}
signals:
void progressUpdated(int value);
~WorkerThread() {
qDebug() << "Thread encerrada e recursos liberados.";
}
};
No widget principle:
Widget::Widget(QWidget *parent)
: QWidget(parent), ui(new Ui::Widget) {
ui->setupUi(this);
worker = new WorkerThread(this);
connect(worker, &WorkerThread::progressUpdated,
[this](int val) {
ui->lcdNumber->display(QString::number(val));
});
}
void Widget::on_pushButton_clicked() {
worker->start();
}
Widget::~Widget() {
worker->quit(); // Sinaliza saída
worker->wait(); // Espera término
delete worker;
}
Utilizando moveToThread para múltiplos trabalhos
Em vez de herdar múltiplas classes de QThread, use moveToThread() para associar objetos a threads distintas:
MyTaskObject* task = new MyTaskObject;
QThread* thread = new QThread;
task->moveToThread(thread);
connect(thread, &QThread::started, task, &MyTaskObject::execute);
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
connect(task, &MyTaskObject::completed, thread, &QThread::quit);
thread->start();
Isso permite que várias tarefas compartilhem uma mesma thread, evitando redundância e melhorando a organização do código.
Pool de Threads com QThreadPool
Para gerenciar múltiplas tarefas de forma eficiente, utilize QThreadPool:
class Task : public QRunnable {
public:
void run() override {
for (int i = 0; i < 5; ++i) {
qDebug() << "Executando tarefa:" << i;
QThread::msleep(500);
}
}
};
// Uso
QThreadPool::globalInstance()->setMaxThreadCount(4);
QThreadPool::globalInstance()->start(new Task());
QThreadPool::globalInstance()->waitForDone();
Essa abordagem automatiza a criação, reutilização e limpeza de threads, otimizando o uso de recursos.
Conceitos Fundamentais
- join vs detach:
join()bloqueia até o término do thread;detach()libera o controle, mas exige cuidado com o tempo de vida do thread. - Funções anônimas (lambda): Expressões lambda permitem definir funções inline com acesso a variáveis do escopo externo.
- Operador ()(): Em classes com
operator(), tornam-se funções invocáveis como se fossem funções normais — úteis para threads. - try-catch: Essencial para tratar exceções em threads. O bloco
tryenvolve código potencialmente falho;catchtrata erros. - Métodos de criação de thread: Funções regulares, lambdas, funções-objeto (functors), e membros de classe podem ser usados como alvos de thread.