前言
以前我多線程使用傳統的繼承qthread重寫run()或者繼承qrunable類把對象丟到線程池解決。經過昨天的面試讓我了解到新的技術,我之前看到過只不過沒有詳細的去了解movetotread技術,這個技術是qt5推出的,qt6還在延續使用
代碼結構
以下是簡單的movetotread的實現
class Worker:public QObject{
Q_OBJECT
private slots:
dowork();/*業務邏輯*/
}//使用方式
QThread thread;
Worker woker;
woker.movetotread(&tread);
thread.start();
//通過信號觸發work
QObject::connect(&sender,&sender::startsignal,&worker,&worker::doworke);
實現邏輯
movetotread實現了線程與事務相分離,但需要運行某個事件的時候只需要將繼承了QObject類的對象通過movetotread(&目標線程)傳進去執行,通過qdequeueconnect()實現跨線程通訊。
關鍵操作
初次創建線程的時候通過start()函數,它會調用run()函數并啟動事件循環(默認exec())
退出線程有三種
thread->quit();//優雅退出
tread->wait();//主線程阻塞等待子線程結束
thread->terminate();//強制退出線程,不推薦這種,可能導致線程未釋放
創建好線程以后可以寫兩個connect管理對象和線程當線程執行完時釋放finished信號來管理對象和線程的生命周期
connect(thread,&qthread::finished,worker,&QObject::deletelater);
connect(thread,&qthread::finished,thread,&QObject::deletelater);
跨線程的時候不要直接調用對象內的方法,可以通過主線程connect信號觸發worker->doworker();
其他注意細節
如果線程未被釋放,是可以通過movetothread將新的對象傳進去。
如果向一個線程里面重復movetothread,最新的對象會把上一個對象給替代掉。
小實驗
我設計兩個線程,一個是主線程,里面是mainwindow,一個是子線程,專門生產1-6的隨機數,生產好了以后將生成的數字傳遞到主線程顯示出來
首先是建一個隨機數生成器類randomnum
//頭文件
#ifndef RANDOMNUM_H
#define RANDOMNUM_H
#include<QObject>
#include<QRandomGenerator>
#include<QTimer>class randomnum : public QObject
{Q_OBJECT
public:explicit randomnum(QObject *parent = nullptr);
public slots:void startrandom();//開始生成隨機數void stoprandom();//暫停生成
signals:void newnumber(int count,int number);
private:QTimer *timer;//計時器int count=0;//計數QRandomGenerator random;//隨機數生成器
};#endif // RANDOMNUM_H
//cpp
#include "randomnum.h"
#include "qobject.h"randomnum::randomnum(QObject *parent): QObject{parent}
{timer=new QTimer(this);connect(timer,&QTimer::timeout,this,[this](){count++;int num=random.bounded(1,6);emit newnumber(count,num);});
}void randomnum::startrandom()
{timer->start(1000);
}void randomnum::stoprandom()
{timer->stop();
}
主程序
//頭文件
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();private:Ui::MainWindow *ui;QThread *thread1;private slots:void appendnum(int count,int num);};
#endif // MAINWINDOW_H
//cpp文件
#include "randomnum.h"
#include "ui_mainwindow.h"
#include<QString>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);ui->plainTextEdit->setReadOnly(true);//創建工作對象和線程randomnum *random1=new randomnum;thread1=new QThread(this);//把工作對象移動到子線程random1->moveToThread(thread1);//連接信號槽//觸發開始隨機數的信號//connect(thread1,&QThread::started,random1,&randomnum::startrandom);//把子線程生成的信號參數傳給主線程對應的槽函數connect(random1,&randomnum::newnumber,this,&MainWindow::appendnum);//開始按鈕connect(ui->btnstart,&QPushButton::clicked,random1,&randomnum::startrandom);//暫停按鈕connect(ui->btnstop,&QPushButton::clicked,random1,&randomnum::stoprandom);//管理對象生命周期connect(thread1,&QThread::finished,random1,&QObject::deleteLater);//connect(thread1,&QThread::finished,thread1,&QObject::deleteLater);thread1->start();
}MainWindow::~MainWindow()
{if(thread1&&thread1->isRunning()){thread1->quit();thread1->wait();}delete ui;
}void MainWindow::appendnum(int count, int num)
{QString str=QString("第%1次,數字是%2").arg(count).arg(num);ui->plainTextEdit->appendPlainText(str);
}
顯示效果
程序怎么實現的看上面代碼就行了,這里講下遇到的問題:
1.關閉程序,程序崩潰,子線程沒有停止導致的,剛開始的時候明明有一個connect連接finished的信號,結果還是沒關掉,原因是deletlater只是標記了他為待刪除,不需要這一句,直接在析構函數里面顯示刪除,之前那一句不要了,避免重復刪除
2.添加開始/停止按鈕,用這個直接操作計時器,不是操控線程就可以實現?