Qt多线程的⼏种实现⽅式
Qt多线程的实现⽅式有:
1. 继承QThread类,重写run()⽅法
2. 使⽤moveToThread将⼀个继承QObject的⼦类移⾄线程,内部槽函数均在线程中执⾏
3. 使⽤QThreadPool,搭配QRunnable(线程池)
4. 使⽤QtConcurrent(线程池)
为什么要⽤线程池?
创建和销毁线程需要和OS交互,少量线程影响不⼤,但是线程数量太⼤,势必会影响性能,使⽤线程池可以减少这种开销。
⼀、继承QThread类,重写run()⽅法
缺点:
  1. 每次新建⼀个线程都需要继承QThread,实现⼀个新类,使⽤不太⽅便。
  2. 要⾃⼰进⾏资源管理,线程释放和删除。并且频繁的创建和释放会带来⽐较⼤的内存开销。
适⽤场景:QThread适⽤于那些常驻内存的任务。
1//mythread.h
2 #ifndef MYTHREAD_H
3#define MYTHREAD_H
4
5 #include <QThread>
6
7class MyThread : public QThread
8 {
9public:
10    MyThread();
11void stop();
12
13protected:
14void run();
15
16private:
17volatile bool stopped;
18 };
19
20#endif// MYTHREAD_H
1//mythread.cpp
2 #include "mythread.h"
3 #include <QDebug>
4 #include <QString>
5
6 MyThread::MyThread()
7 {
8    stopped = false;
9 }
10
11
12
13void MyThread::stop()
14 {
15    stopped = true;
16 }
17
18
19
20void MyThread::run()
21 {
22    qreal i = 0;
23
24while( !stopped )
25    {
26        qDebug() << QString("in MyThread: %1").arg(i);
27        sleep(1);
28        i++;
29    }
30    stopped = false;
31 }
1//widget.h
2 #ifndef WIDGET_H
3#define WIDGET_H
4
5 #include <QWidget>
6 #include "mythread.h"
7
8
9 QT_BEGIN_NAMESPACE
10namespace Ui { class Widget; }
11 QT_END_NAMESPACE
12
13class Widget : public QWidget
14 {
15    Q_OBJECT
16
17public:
18    Widget(QWidget *parent = nullptr);
19    ~Widget();
20
21private slots:
22void on_startBut_clicked();
23
24void on_stopBut_clicked();
25
26private:
27    Ui::Widget *ui;
28    MyThread thread;
29 };
30#endif// WIDGET_H
1//widget.cpp
2
3 #include "widget.h"
4 #include "ui_widget.h"
5
6 Widget::Widget(QWidget *parent)
7    : QWidget(parent)
8    , ui(new Ui::Widget)
9 {
10    ui->setupUi(this);
11    ui->startBut->setEnabled(true);
12    ui->stopBut->setEnabled(false);
13 }
14
15 Widget::~Widget()
16 {
17delete ui;
18 }
19
20
21void Widget::on_startBut_clicked()
22 {
23    thread.start();
24    ui->startBut->setEnabled(false);
25    ui->stopBut->setEnabled(true);
26 }
27
28void Widget::on_stopBut_clicked() 29 {
30if( thread.isRunning() )
31    {
32        thread.stop();
33        ui->startBut->setEnabled(true);
34        ui->stopBut->setEnabled(false);
35    }
36 }
⼆、使⽤moveToThread将⼀个继承QObject的⼦类移⾄线程
更加灵活,不需要继承QThread,不需要重写run⽅法,适⽤于复杂业务的实现。注意,该业务类的不同槽函数均在同⼀个线程中执⾏。
1//worker.h
2 #ifndef WORKER_H
3#define WORKER_H
4
5 #include <QObject>
6
7class Worker : public QObject
8 {
9    Q_OBJECT
10
11public:
12    Worker();
13
14    ~Worker();
15
16public slots:
17void doWork();
18
19void another();
20
21 signals:
22void stopWork();
23
24 };
25
26#endif// WORKER_H
1//worker.cpp
2 #include "worker.h"
3 #include <QDebug>
4 #include <QThread>
5
6 Worker::Worker()
7 {
8
9 }
10
11
12 Worker::~Worker()
13 {
14
15 }
16
17
18void Worker::doWork()
19 {
20    qDebug() << "current thread id is " << QThread::currentThreadId();
21    emit stopWork();
22 }
23
24
25void Worker::another()
26 {
27    qDebug() << "another current thread id is " << QThread::currentThreadId(); 28//emit stopWork();
29 }
1//dialog.h
2 #ifndef DIALOG_H
3#define DIALOG_H
4
5 #include <QDialog>
6 #include <QThread>
7 #include "worker.h"
8
9 QT_BEGIN_NAMESPACE
10namespace Ui { class Dialog; }
11 QT_END_NAMESPACE
12
include意思
13class Dialog : public QDialog
14 {
15    Q_OBJECT
16
17public:
18    Dialog(QWidget *parent = nullptr);
19    ~Dialog();
20
21 signals:
22void startWork();
23void startAnother();
24
25public slots:
26void endThread();
27
28private:
29    Ui::Dialog *ui;
30    QThread *m_pThread;
31    Worker *m_pWorker;
32 };
33#endif// DIALOG_H
1//dialog.cpp
2 #include "dialog.h"
3 #include "ui_dialog.h"
4 #include <QDebug>
5
6 Dialog::Dialog(QWidget *parent)
7    : QDialog(parent)
8    , ui(new Ui::Dialog)
9 {
10    ui->setupUi(this);
11
12    m_pThread = new QThread();
13    m_pWorker = new Worker();
14
15    connect(this, &Dialog::startWork, m_pWorker, &Worker::doWork);
16    connect(this, &Dialog::startAnother, m_pWorker, &Worker::another);
17    connect(m_pWorker, &Worker::stopWork, this, &Dialog::endThread);
18    m_pWorker->moveToThread(m_pThread);
19    m_pThread->start();
20    emit startWork();
21    emit startAnother();
22 }
23
24 Dialog::~Dialog()
25 {
26delete ui;
27delete m_pThread;
28delete m_pWorker;
29 }
30
31
32void Dialog::endThread()
33 {
34    qDebug() << "endThread";
35    m_pThread->quit();
36    m_pThread->wait();
37 }
不过我为什么要⽤界⾯类呢?搞不懂!
三、使⽤QThreadPool,搭配QRunnable
QRunnable常⽤接⼝:
  bool QRunnable::autoDelete() const;
  void QRunnable::setAutoDelete(bool autoDelete);
QRunnable常⽤函数不多,主要设置其传到底给线程池后,是否需要⾃动析构;
若该值为false,则需要程序员⼿动析构,要注意内存泄漏;
QThreadPool常⽤接⼝:
  void QThreadPool::start(QRunnable * runnable, int priority = 0);
  bool QThreadPool::tryStart(QRunnable * runnable);
start()预定⼀个线程⽤于执⾏QRunnable接⼝,当预定的线程数量超出线程池的最⼤线程数后,QRunnable接⼝将会进⼊队列,等有空闲线程后,再执⾏;
priority指定优先级
tryStart()和start()的不同之处在于,当没有空闲线程后,不进⼊队列,返回false
业务类需要继承QRunnable,并且重写run()⽅法。注意,QRunnbale不是QObject的⼦类,可以发射信号,但⽤不了槽函数。
优点:⽆需⼿动释放资源,QThreadPool启动线程执⾏完成后会⾃动释放。
缺点:不能使⽤信号槽与外界通信。
适⽤场景:QRunnable适⽤于线程任务量⽐较⼤,需要频繁创建线程。QRunnable能有效减少内存开销
1//myrunnable.h
2 #ifndef MYRUNNABLE_H