Qt 提供跨平台線程支援,核心類包括 QThread、線程安全的事件傳遞(信號槽)、全域互斥鎖。本文通過實例演示如何將耗時計算移出 GUI 線程,並探討 OpenGL 渲染線程分離技巧。
QWidget, QPixmap 等) 必須在此線程創建/操作。點擊「計算 Pi」按鈕,主線程執行億次循環,界面無響應,甚至無法關閉視窗。
QThread,重寫 run() 執行計算。computeFinish(double) 發送結果。slotShowResult(double) 更新標籤。thread->start() 啟動線程。// ComputeThread.h
class ComputeThread : public QThread {
Q_OBJECT
protected:
void run() override;
signals:
void computeFinish(double result);
};
// ComputeThread.cpp
void ComputeThread::run() {
double result = 0;
for (int i = 1; i <= 1000000000; ++i) {
double val = 4.0 / (2*i - 1);
result += (i % 2) ? val : -val;
}
emit computeFinish(result);
}
// MainWindow.cpp
connect(computeThread, &ComputeThread::computeFinish, this, &MainWindow::slotShowResult);
connect(btnCalc, &QPushButton::clicked, computeThread, &QThread::start);
結果:計算期間界面流暢,「Say Hi」按鈕即時響應。
場景:QGLWidget 需持續動畫(如旋轉三角形),主線程死循環會阻塞事件處理。
GLThreadGLThread 持有 GLWidget*,在 run() 中調用 makeCurrent() 綁定 OpenGL 上下文。glClear → 繪製 → swapBuffers() → msleep(50) 控制幀率。resizeViewport() 通過標誌位 doResize 線程安全傳遞新尺寸。GLWidget 轉發 resizeEvent 到線程,paintEvent 留空(由線程接管)。void GLThread::run() {
glw->makeCurrent();
// 初始化投影矩陣...
while (doRendering) {
if (doResize) { /* 更新 viewport & 投影 */ doResize = false; }
rotAngle += 5; if (rotAngle >= 360) rotAngle = 0;
// 繪製三角形...
glw->swapBuffers();
msleep(50);
}
}
| 方式 | 適用場景 | 注意事項 |
|---|---|---|
| 信號槽 (QueuedConnection) | 跨線程傳遞數據、觸發 GUI 更新 | 線程安全,參數需註冊元類型 |
| 指針直接訪問 | 高頻、低延遲共享數據(如 OpenGL 上下文) | 需手動加鎖 (QMutex),避免競態 |
QMetaObject::invokeMethod | 跨線程調用槽函數 | 支援同步/異步 |
makeCurrent()。quit() + wait() 確保資源釋放。QMutex/QReadWriteLock 保護,或用無鎖隊列傳遞命令。QThread,更靈活。