![](/img/trans.png)
[英]Running a long operation in QThread and sending signals to the main thread still freezes the UI
[英]QThread::create running on UI Thread
我的應用程序需要多線程。 根據 Qt 的文檔,有很多方法可以實現這一點。
簡要概述QThread
ing方法:
子類 QThread 並重新實現 run() ( doc )。
創建一個 object 繼承自QObject
並使用Q_OBJECT
宏(用於信號/插槽)和doWork
方法,創建一個QThread
object,使用QObject::moveToThread(QThread*)並調用QThread::start()
(文檔,維基)
對 lambda 語法使用QThread::create(Function &&f)或QThread::create(std::function<>)
。
我選擇使用第三個選項進行測試。 我還基於選項 2 實現了一個名為QThreading的多線程庫,使用QWorkerThread充當 controller object, QThread
和QObject
作為對象。 這個庫也給出了相同的結果,如下所示。
既然文檔已經不在了,問題就來了。
使用QThread::create(Function &&f)
,我正在測試 QThread 是否與 UI 線程分開運行。 下面的MCVE示例:
#include <QCoreApplication>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qsrand(0);
QThread *thread = nullptr;
auto threadTest = std::function<void ()> ([&]() {
if(thread->thread() == a.thread()) {
qDebug() << "UI Thread in use";
} else {
qDebug() << "Worker thread in use";
}
for (int var = 0; var < INT_MAX; ++var) {
int r = qrand() % 100;
thread->thread()->msleep(r);
qDebug() << "[Worker Thread " << thread->thread()->currentThreadId() << "] " << r;
}
});
thread = QThread::create(threadTest);
thread->start();
for (int var = 0; var < INT_MAX; ++var) {
// sleep thread 0-100ms
int r = qrand() % 100;
a.thread()->msleep(r);
qDebug() << "[UI Thread " << a.thread()->currentThreadId() << "] " << r;
}
return a.exec();
}
output 是:
UI Thread in use
[Worker Thread 0x47e4 ] 41
[UI Thread 0x10c0 ] 38
[UI Thread 0x10c0 ] 19
[UI Thread 0x10c0 ] 38
[Worker Thread 0x47e4 ] 67
[UI Thread 0x10c0 ] 37
[Worker Thread 0x47e4 ] 34
[Worker Thread 0x47e4 ] 0
[UI Thread 0x10c0 ] 55
[Worker Thread 0x47e4 ] 69
[Worker Thread 0x47e4 ] 24
[UI Thread 0x10c0 ] 97
[Worker Thread 0x47e4 ] 78
[UI Thread 0x10c0 ] 65
[Worker Thread 0x47e4 ] 58
[UI Thread 0x10c0 ] 85
[Worker Thread 0x47e4 ] 62
[UI Thread 0x10c0 ] 50
[Worker Thread 0x47e4 ] 64
[UI Thread 0x10c0 ] 12
[Worker Thread 0x47e4 ] 5
[Worker Thread 0x47e4 ] 45
注意事項
UI 線程 ID: 0x10c0
工作線程 ID: 0x47e4
擔憂來自哪里
正在使用的 UI 線程
讓我困惑的是不同的線程地址,但工作線程仍然等於UI 線程。
這讓我有兩種解釋:
QThread::currentThread
總是返回主機/主線程(難以置信,使得 function 有點毫無意義)
QThread *thread
位於主 UI 線程內,因此(獲取父線程)始終返回父線程,即ParentThread == WorkerThread (ParentThread)
我是否想了解QThread
的工作原理?
if(QCoreApplication::instance()->thread() == QThread::currentThread()) {
qDebug() << "UI Thread in use";
} else {
qDebug() << "Worker thread in use";
}
詳細說明
QThread
object 管理程序中的一個控制線程。 QThreads 在run()
中開始執行。 默認情況下,run() 通過調用 exec() 啟動事件循環,並在線程內運行 Qt 事件循環。
此外,QThread 是一個 QObject ,它存在於父線程中,或者如果它沒有父線程,則它被創建的地方,正如文檔指出的那樣:
線程親和力
QObject 實例被稱為具有線程親和性,或者說它存在於某個線程中。 當 QObject 接收到排隊的信號或發布的事件時,槽或事件處理程序將在 object 所在的線程中運行。
注意:如果 QObject 沒有線程關聯(即,如果 thread() 返回零),或者如果它存在於沒有運行事件循環的線程中,則它無法接收排隊信號或發布的事件。
默認情況下,QObject 存在於創建它的線程中。 可以使用 thread() 查詢對象的線程親和性,並使用 moveToThread() 更改對象的線程親和性。
所有 QObject 必須與其父對象存在於同一線程中。 最后:
- 如果涉及的兩個 QObject 存在於不同的線程中,setParent() 將失敗。
- 當一個 QObject 被移動到另一個線程時,它的所有子對象也將被自動移動。
- 如果 QObject 有父對象,則 moveToThread() 將失敗。
- 如果 QObjects 在 QThread::run() 中創建,它們不能成為 QThread object 的子級,因為 QThread 不存在於調用 QThread::run() 的線程中。
注意: QObject 的成員變量不會自動成為它的子變量。 必須通過將指針傳遞給子構造函數或調用 setParent() 來設置父子關系。 如果沒有這一步,調用 moveToThread() 時對象的成員變量將保留在舊線程中。
在您的情況下,“線程” object 是一個 QObject ,它存在於主線程中,因為它沒有父線程並且在那里創建並處理另一個線程。
MWE
#include <QCoreApplication>
#include <QDebug>
#include <QThread>
#include <QTimer>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qsrand(0);
auto threadTest = std::function<void ()> ([&]() {
if(QCoreApplication::instance()->thread() == QThread::currentThread()) {
qDebug() << "UI Thread in use";
} else {
qDebug() << "Worker thread in use";
}
for (int var = 0; var < INT_MAX; ++var) {
int r = qrand() % 100;
QThread::msleep(r);
qDebug() << "[Worker Thread " << QThread::currentThreadId() << "] " << r;
}
});
QThread *thread = QThread::create(threadTest);
thread->start();
int var = 0;
std::function<void ()> timerTest;
timerTest = [&](){
int r = qrand() % 100;
qDebug() << "[UI Thread " << QThread::currentThreadId() << "] " << r;
++var;
if (var < INT_MAX)
QTimer::singleShot(r, timerTest);
};
int r = qrand() % 100;
QTimer::singleShot(r, timerTest);
return a.exec();
}
Output:
Worker thread in use
[UI Thread 0x7fc6222993c0 ] 94
[Worker Thread 0x7fc621f62700 ] 71
[UI Thread 0x7fc6222993c0 ] 86
[Worker Thread 0x7fc621f62700 ] 94
[UI Thread 0x7fc6222993c0 ] 37
[Worker Thread 0x7fc621f62700 ] 86
...
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.