#():線程簡介
前面我們討論了有關進程以及進程間通訊的相關問題,現在我們開始討論線程。事實上,現代的程序中,使用線程的概率應該大于進程。特別是在多核時代,隨著 CPU 主頻的提升,受制于發熱量的限制,CPU 散熱問題已經進入瓶頸,另辟蹊徑地提高程序運行效率就是使用線程,充分利用多核的優勢。有關線程和進程的區別已經超出了本章的范疇,我們簡單提一句,一個進程可以有一個或更多線程同時運行。線程可以看做是“輕量級進程”,進程完全由操作系統管理,線程即可以由操作系統管理,也可以由應用程序管理。
Qt?使用`QThread`?來**管理**線程。下面來看一個簡單的例子:
~~~
///!!! Qt5
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QWidget *widget = new QWidget(this);
QVBoxLayout *layout = new QVBoxLayout;
widget->setLayout(layout);
QLCDNumber *lcdNumber = new QLCDNumber(this);
layout->addWidget(lcdNumber);
QPushButton *button = new QPushButton(tr("Start"), this);
layout->addWidget(button);
setCentralWidget(widget);
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, [=]() {
static int sec = 0;
lcdNumber->display(QString::number(sec++));
});
WorkerThread *thread = new WorkerThread(this);
connect(button, &QPushButton::clicked, [=]() {
timer->start(1);
for (int i = 0; i < 2000000000; i++);
timer->stop();
});
}
~~~
我們的主界面有一個用于顯示時間的 LCD 數字面板還有一個用于啟動任務的按鈕。程序的目的是用戶點擊按鈕,開始一個非常耗時的運算(程序中我們以一個 2000000000 次的循環來替代這個非常耗時的工作,在真實的程序中,這可能是一個網絡訪問,可能是需要復制一個很大的文件或者其它任務),同時 LCD 開始顯示逝去的毫秒數。毫秒數通過一個計時器`QTimer`進行更新。計算完成后,計時器停止。這是一個很簡單的應用,也看不出有任何問題。但是當我們開始運行程序時,問題就來了:點擊按鈕之后,程序界面直接停止響應,直到循環結束才開始重新更新。
有經驗的開發者立即指出,這里需要使用線程。這是因為 Qt?中所有界面都是在 UI?線程中(也被稱為主線程,就是執行了`QApplication::exec()`的線程),在這個線程中執行耗時的操作(比如那個循環),就會阻塞 UI?線程,從而讓界面停止響應。界面停止響應,用戶體驗自然不好,不過更嚴重的是,有些窗口管理程序會檢測到你的程序已經失去響應,可能會建議用戶強制停止程序,這樣一來你的程序可能就此終止,任務再也無法完成。所以,為了避免這一問題,我們要使用 QThread?開啟一個新的線程:
~~~
///!!! Qt5
class WorkerThread : public QThread
{
Q_OBJECT
public:
WorkerThread(QObject *parent = 0)
: QThread(parent)
{
}
protected:
void run()
{
for (int i = 0; i < 1000000000; i++);
emit done();
}
signals:
void done();
};
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QWidget *widget = new QWidget(this);
QVBoxLayout *layout = new QVBoxLayout;
widget->setLayout(layout);
lcdNumber = new QLCDNumber(this);
layout->addWidget(lcdNumber);
QPushButton *button = new QPushButton(tr("Start"), this);
layout->addWidget(button);
setCentralWidget(widget);
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, [=]() {
static int sec = 0;
lcdNumber->display(QString::number(sec++));
});
WorkerThread *thread = new WorkerThread(this);
connect(thread, &WorkerThread::done, timer, &QTimer::stop);
connect(thread, &WorkerThread::finished, thread, &WorkerThread::deleteLater);
connect(button, &QPushButton::clicked, [=]() {
timer->start(1);
thread->start();
});
}
~~~
注意,我們增加了一個`WorkerThread`類。`WorkerThread`繼承自`QThread`類,重寫了其`run()`函數。我們可以認為,`run()`函數就是新的線程需要執行的代碼。在這里就是要執行這個循環,然后發出計算完成的信號。而在按鈕點擊的槽函數中,使用`QThread::start()`函數啟動一個線程(注意,這里不是`run()`函數)。再次運行程序,你會發現現在界面已經不會被阻塞了。另外,我們將`WorkerThread::deleteLater()`函數與`WorkerThread::finished()`信號連接起來,當線程完成時,系統可以幫我們清除線程實例。這里的`finished()`信號是系統發出的,與我們自定義的`done()`信號無關。
這是 Qt?線程的最基本的使用方式之一(確切的說,這種使用已經不大推薦使用,不過因為看起來很清晰,而且簡單使用起來也沒有什么問題,所以還是有必要介紹)。代碼看起來很簡單,不過,如果你認為 Qt 的多線程編程也很簡單,那就大錯特錯了。Qt?多線程的優勢設計使得它使用起來變得容易,但是坑很多,稍不留神就會被絆住,尤其是涉及到與 QObject 交互的情況。稍懂多線程開發的童鞋都會知道,調試多線程開發簡直就是煎熬。下面幾章,我們會更詳細介紹有關多線程編程的相關內容。
- (1)序
- (2)Qt 簡介
- (3)Hello, world!
- (4)信號槽
- (5)自定義信號槽
- (6)Qt 模塊簡介
- (7)MainWindow 簡介
- (8)添加動作
- (9)資源文件
- (10)對象模型
- (11)布局管理器
- (12)菜單欄、工具欄和狀態欄
- (13)對話框簡介
- (14)對話框數據傳遞
- (15)標準對話框 QMessageBox
- (16)深入 Qt5 信號槽新語法
- (17)文件對話框
- (18)事件
- (19)事件的接受與忽略
- (21)事件過濾器
- (22)事件總結
- (23)自定義事件
- (24)Qt 繪制系統簡介
- (25)畫刷和畫筆
- (26)反走樣
- (27)漸變
- (28)坐標系統
- (29)繪制設備
- (30)Graphics View Framework
- (31)貪吃蛇游戲(1)
- (32)貪吃蛇游戲(2)
- (33)貪吃蛇游戲(3)
- (34)貪吃蛇游戲(4)
- (35)文件
- (36)二進制文件讀寫
- (37)文本文件讀寫
- (38)存儲容器
- (39)遍歷容器
- (40)隱式數據共享
- (41)model/view 架構
- (42)QListWidget、QTreeWidget 和 QTableWidget
- (43)QStringListModel
- (44)QFileSystemModel
- (45)模型
- (46)視圖和委托
- (47)視圖選擇
- (48)QSortFilterProxyModel
- (49)自定義只讀模型
- (50)自定義可編輯模型
- (51)布爾表達式樹模型
- (52)使用拖放
- (53)自定義拖放數據
- (54)剪貼板
- (55)數據庫操作
- (56)使用模型操作數據庫
- (57)可視化顯示數據庫數據
- (58)編輯數據庫外鍵
- (59)使用流處理 XML
- (60)使用 DOM 處理 XML
- (61)使用 SAX 處理 XML
- (62)保存 XML
- (63)使用 QJson 處理 JSON
- (64)使用 QJsonDocument 處理 JSON
- (65)訪問網絡(1)
- (66)訪問網絡(2)
- (67)訪問網絡(3)
- (68)訪問網絡(4)
- (69)進程
- (70)進程間通信
- (71)線程簡介
- (72)線程和事件循環
- (73)Qt 線程相關類
- (74)線程和 QObject
- (75)線程總結
- (76)QML 和 QtQuick 2
- (77)QML 語法
- (78)QML 基本元素
- (79)QML 組件
- (80)定位器
- (81)元素布局
- (82)輸入元素
- (83)Qt Quick Controls
- (84)Repeater
- (85)動態視圖
- (86)視圖代理
- (87)模型-視圖高級技術
- (88)Canvas
- (89)Canvas(續)