一、Qt多線程兩種方式:
方式1:繼承自QThread類,重載run函數(shù)。此實現(xiàn)方法只有run()函數(shù)內(nèi)的代碼是運行在子線程內(nèi)。(不使用事件循環(huán))
使用方法:
(1)run函數(shù)內(nèi)有一個while或for的死循環(huán)(模擬耗時操作);
(2)通過一個標記位來控制死循環(huán)的退出;
(3)run()函數(shù)中無限循環(huán)記得強制休息,如果不加就會造成運行后CPU占用率100%的問題。一定要運行sleep將時間片讓出去一下,處理其他事物;
(4)run()對于線程的作用相當于main函數(shù)對于應用程序。它是線程的入口,run的開始和結(jié)束意味著線程的開始和結(jié)束;
(5)在調(diào)用start()之后,新創(chuàng)建的線程就會調(diào)用run函數(shù),默認實現(xiàn)調(diào)用exec()。
使用場景:
適用于后臺執(zhí)行長時間的耗時操作,如文件復制、網(wǎng)絡(luò)數(shù)據(jù)讀取。
代碼示例:
#ifndef QDEMOTHREAD_H
#define QDEMOTHREAD_H
#include <QThread>
#include <QDebug>
class QDemoThread : public QThread
{
Q_OBJECT
public:
QDemoThread(QObject* parent = nullptr);
~QDemoThread();
protected:
void run() override;
public:
void stop();
private:
bool flag;
};
#endif // QDEMOTHREAD_H
#include "qdemothread.h"
QDemoThread::QDemoThread(QObject* parent) : QThread(parent)
{
}
QDemoThread::~QDemoThread()
{
}
void QDemoThread::run()
{
flag = true;
while(flag)
{
qDebug() << "thread id:" << QThread::currentThreadId();
sleep(1); //此處必須強制休息,否則CPU占用率很大
}
}
void QDemoThread::stop()
{
flag = false;
if(isRunning())
{
exit(); // 結(jié)束線程
wait(); // 等待退出
}
}
方式二(推薦):
創(chuàng)建一個QThread和QWorker(繼承自QObject)類對象,使用moveToThread函數(shù)移動到thread中運行,通過thread類start信號和worker的init槽函數(shù)綁定,init槽函數(shù)內(nèi)是一些初始化操作,然后定義個定時器,周期觸發(fā)doWork()。(使用事件循環(huán))
使用信號和槽時根本不用考慮多線程的存在。也不用使用QMutex來進行同步,Qt的事件循環(huán)會自己自動處理好這個。
網(wǎng)上有很多教程是在doWork()中使用while(isRunning)死循環(huán)的形式,不建議這么干,如果線程一直在doWork中死循環(huán),那么他是無法接收到來自外部的信號的。推薦的方法是用定時器周期觸發(fā)。
使用場景:
適用于事務(wù)性操作,如文件讀寫、數(shù)據(jù)庫讀寫。
適合單次任務(wù)執(zhí)行,即有點像懶人,觸發(fā)一下,干一次活;
適合干完活,需要主動推送一個通知;
適合用于簡化多線程中,對數(shù)據(jù)安全的保護。
不適用高頻率任務(wù),即跑完一個任務(wù),可能沒有時間休息,持續(xù)跑。
執(zhí)行高頻率任務(wù),還是需要使用重寫QThread::run()的方式,來實現(xiàn)。
代碼示例:
QWorker類:
#ifndef QWORKER_H
#define QWORKER_H
#include <QObject>
#include <QThread>
#include <QDebug>
class QWorker : public QObject
{
Q_OBJECT
public:
explicit QWorker(QObject *parent = nullptr);
~QWorker();
signals:
void newData(QByteArray data); // 將本類內(nèi)的私有數(shù)據(jù)通過該信號發(fā)射出去,供外部使用
public slots:
void init(); // 一些必要的初始化操作寫在此函數(shù)內(nèi)
void doWork(); // 一些耗時操作寫在此函數(shù)內(nèi)
void writeData(const char* buf, qint64 len); // 供外部使用的操作接口
};
#endif // QWORKER_H
#include "qworker.h"
QWorker::QWorker(QObject *parent) : QObject(parent)
{
qDebug() << __FUNCTION__ << "Thread ID:" << QThread::currentThreadId();
}
QWorker::~QWorker()
{
qDebug() << __FUNCTION__ << "Thread ID:" << QThread::currentThreadId();
}
void QWorker::init()
{
qDebug() << __FUNCTION__ << "Thread ID:" << QThread::currentThreadId();
}
void QWorker::doWork()
{
static int count = 0;
qDebug() << __FUNCTION__ << "Thread ID:" << QThread::currentThreadId() << ">>>" << count++;
}
void QWorker::writeData(const char* buf, qint64 len)
{
qDebug() << __FUNCTION__ << "Thread ID:" << QThread::currentThreadId() << ">>>" << QByteArray(buf, len);
}
主線程:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "qworker.h"
#include <QTimer>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
QWorker* worker;
QThread* thread;
QTimer* timer;
signals:
void writeData(const char* buf, qint64 len);
void stopWork();
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
qDebug() << __FUNCTION__ << "Thread ID:" << QThread::currentThreadId();
timer = new QTimer(this);
thread = new QThread(); // 不要指定parent
worker = new QWorker(); // 不要指定parent
// thread的finished和deleteLater相連接后,在thread退出時自動刪除thread對象,無需手動delete thread
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
// thread的finished和worker的deleteLater相連接后,在thread退出時自動刪除worker對象,無需手動delete worker
connect(thread, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(started()), worker, SLOT(init()));
connect(timer, SIGNAL(timeout()), worker, SLOT(doWork()));
connect(this, SIGNAL(writeData(const char*,qint64)), worker, SLOT(writeData(const char*,qint64)));
worker->moveToThread(thread);
thread->start();
timer->start(1000); // 1000ms執(zhí)行一次doWork()
}
MainWindow::~MainWindow()
{
if(timer->isActive())
{
timer->stop();
}
if(thread->isRunning())
{
thread->quit();
thread->wait();
}
qDebug() << __FUNCTION__ << "Thread ID:" << QThread::currentThreadId();
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
emit writeData("hello world\r\n", 13);
}
二、QT線程同步
線程同步:Qt中使用多線程時候,多線程的同步就是一個不可避免的問題。
多線程的同步就是使多個線程在同時執(zhí)行同一段代碼的時候,有順序的執(zhí)行,不會出現(xiàn)同時有兩個或者多個線程執(zhí)行同一段代碼的情況,特別是在對變量或者文件執(zhí)行寫操作的時候。也就是所謂的線程安全,線程安全指的是這段代碼在一個線程或者多個線程執(zhí)行的過程中,不加同步機制或者任何其他代碼,執(zhí)行的結(jié)果是一樣的,這就是線程安全。
在Qt中常用的同步方法是使用鎖機制,但是如果使用的方法不對或者時機不對同樣也不會起到線程同步的效果。
例如: 有兩個進程都對一個變量進行讀寫操作,這時就需要互斥量來進行線程同步,這個互斥量必須是全局的,不然如果各自在.cpp中聲明一個QMutex mutex;是沒有任何意義的。因為:每個線程對象都創(chuàng)建了一個QMutex對象,兩個線程在運行過程中各自執(zhí)行的是自己的鎖,所以每個鎖有且只有一個線程在執(zhí)行。它們是互不干擾的,所有雖然加鎖了,也沒有起到同步的作用。
代碼示例:
主程序:
// 多線程方式1
myThread = new MyThread(); // 繼承自QThread
myThread->start();
// 多線程方式2
myThread1 = new MyThread1(); // 繼承自QObject
thread1 = new QThread();
connect(thread1, SIGNAL(started(), myThread1 , SLOT(doWork)));
myThread1->moveToThread(thread1 );
thread1->start();
線程1:MyThread.cpp文章來源:http://www.zghlxwxcb.cn/news/detail-409226.html
#include "mythread.h"
#include <QMutex>
QMutex mutex;
extern int globalCount ;
MyThread::MyThread()
{
isStop = true;
}
void MyThread::closeThread()
{
isStop = true;
}
//重寫基類的run函數(shù)
void MyThread::run()//只有run()里面在新的線程里
{
qDebug()<<tr("mythread QThread::currentThreadId()==")<<QThread::currentThreadId();
isStop = false;
//這種方式還是相當于在主線程里面執(zhí)行
// timer= new QTimer(NULL);
// connect(timer, SIGNAL(timeout()), this, SLOT(onTimeout()));
// timer->start(1000);
// this->exec();
while(1)
{
if(isStop)
{
return;
}
QMutexLocker locker(&mutex);
qDebug()<<"globalCount: " << ++globalCount <<QThread::currentThreadId();
sleep(5);//QThread
}
}
//這種方式還是相當于在主線程里面執(zhí)行
void MyThread::onTimeout()
{
qDebug()<<tr("mythread QThread::currentThreadId()==")<<QThread::currentThreadId();
qDebug()<<"globalCount: " << ++globalCount;
}
線程2:MyThread1.cpp文章來源地址http://www.zghlxwxcb.cn/news/detail-409226.html
#include "mythread1.h"
#include <QMutex>
extern QMutex mutex;
QMutex mutex1;
extern int globalCount ;
MyThread1::MyThread1(QObject *parent) : QObject(parent)
{
isStop = true;
}
void MyThread1::closeThread()
{
isStop = true;
}
void MyThread1::doWork()
{
qDebug()<<tr("mythread2 QThread::currentThreadId()==")<<QThread::currentThreadId();
isStop = false;
//定時方式1:(都是在子線程中)
timer= new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(onTimeout()));
timer->start(1000);
//定時方式2:(都是在子線程中)
// while(1)
// {
// if(isStop)
// {
// return;
// }
// QMutexLocker locker(&mutex);
// qDebug()<<"globalCount: " << ++globalCount <<QThread::currentThreadId();
// Sleep(1000);//<windows.h>,大寫的S,單位是微秒
// }
}
void MyThread1::onTimeout()
{
QMutexLocker locker(&mutex);
qDebug()<<"globalCount: " << ++globalCount <<QThread::currentThreadId();
}
到了這里,關(guān)于Qt多線程使用的兩種方式的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!