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 
 6 
 7 class MyThread : public QThread
 8 {
 9 public:
10     MyThread();
11     void stop();
12 
13 protected:
14     void run();
15 
16 private:
17     volatile bool stopped;
18 };
19 
20 #endif // MYTHREAD_H
 1 //mythread.cpp
 2 #include "mythread.h"
 3 #include 
 4 #include 
 5 
 6 MyThread::MyThread()
 7 {
 8     stopped = false;
 9 }
10 
11 
12 
13 void MyThread::stop()
14 {
15     stopped = true;
16 }
17 
18 
19 
20 void MyThread::run()
21 {
22     qreal i = 0;
23 
24     while( !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 
 6 #include "mythread.h"
 7 
 8 
 9 QT_BEGIN_NAMESPACE
10 namespace Ui { class Widget; }
11 QT_END_NAMESPACE
12 
13 class Widget : public QWidget
14 {
15     Q_OBJECT
16 
17 public:
18     Widget(QWidget *parent = nullptr);
19     ~Widget();
20 
21 private slots:
22     void on_startBut_clicked();
23 
24     void on_stopBut_clicked();
25 
26 private:
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 {
17     delete ui;
18 }
19 
20 
21 void Widget::on_startBut_clicked()
22 {
23     thread.start();
24     ui->startBut->setEnabled(false);
25     ui->stopBut->setEnabled(true);
26 }
27 
28 void Widget::on_stopBut_clicked()
29 {
30     if( 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 
 6 
 7 class Worker : public QObject
 8 {
 9     Q_OBJECT
10 
11 public:
12     Worker();
13 
14     ~Worker();
15 
16 public slots:
17     void doWork();
18 
19     void another();
20 
21 signals:
22     void stopWork();
23 
24 };
25 
26 #endif // WORKER_H
 1 //worker.cpp
 2 #include "worker.h"
 3 #include 
 4 #include 
 5 
 6 Worker::Worker()
 7 {
 8 
 9 }
10 
11 
12 Worker::~Worker()
13 {
14 
15 }
16 
17 
18 void Worker::doWork()
19 {
20     qDebug() << "current thread id is " << QThread::currentThreadId();
21     emit stopWork();
22 }
23 
24 
25 void 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 
 6 #include 
 7 #include "worker.h"
 8 
 9 QT_BEGIN_NAMESPACE
10 namespace Ui { class Dialog; }
11 QT_END_NAMESPACE
12 
13 class Dialog : public QDialog
14 {
15     Q_OBJECT
16 
17 public:
18     Dialog(QWidget *parent = nullptr);
19     ~Dialog();
20 
21 signals:
22     void startWork();
23     void startAnother();
24 
25 public slots:
26     void endThread();
27 
28 private:
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 
 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 {
26     delete ui;
27     delete m_pThread;
28     delete m_pWorker;
29 }
30 
31 
32 void 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);

QThreadPool常用接口:

  void QThreadPool::start(QRunnable * runnable, int priority = 0);

  bool QThreadPool::tryStart(QRunnable * runnable);

业务类需要继承QRunnable,并且重写run()方法。注意,QRunnbale不是QObject的子类,可以发射信号,但用不了槽函数。

优点:无需手动释放资源,QThreadPool启动线程执行完成后会自动释放。
缺点:不能使用信号槽与外界通信。
适用场景:QRunnable适用于线程任务量比较大,需要频繁创建线程。QRunnable能有效减少内存开销

 1 //myrunnable.h
 2 #ifndef MYRUNNABLE_H
 3 #define MYRUNNABLE_H
 4 
 5 #include 
 6 #include 
 7 
 8 
 9 class MyRunnable : public QRunnable
10 {
11 public:
12     MyRunnable(const QString szThreadName);
13     void run();
14 
15 private:
16     QString m_szThreadName;
17 };
18 
19 #endif // MYRUNNABLE_H
 1 //myrunnable.cpp
 2 #include "myrunnable.h"
 3 #include 
 4 #include 
 5 
 6 MyRunnable::MyRunnable(const QString szThreadName) : m_szThreadName(szThreadName)
 7 {
 8 
 9 }
10 
11 
12 void MyRunnable::run()
13 {
14     qDebug() << "Start thread id : " << QThread::currentThreadId();
15     int iCount = 0;
16 
17     while (1)
18     {
19         if(iCount >= 10)
20         {
21             break;
22         }
23 
24         qDebug() << m_szThreadName << " count : " << iCount++;
25         QThread::msleep(500);
26     }
27 }
 1 //mian.cpp
 2 #include 
 3 #include "myrunnable.h"
 4 #include 
 5 
 6 static QThreadPool* g_pThreadPool = NULL;
 7 
 8 int main(int argc, char *argv[])
 9 {
10     QCoreApplication a(argc, argv);
11 
12     MyRunnable* pRunnable1 = new MyRunnable("1# thread");
13     pRunnable1->setAutoDelete(true);
14 
15     MyRunnable* pRunnable2 = new MyRunnable("2# thread");
16     pRunnable2->setAutoDelete(true);
17 
18     g_pThreadPool = QThreadPool::globalInstance();
19 
20     g_pThreadPool->start(pRunnable1);
21     g_pThreadPool->start(pRunnable2);
22 
23     g_pThreadPool = NULL;
24 
25     return a.exec();
26 }

四、使用QtConcurrent

Concurrent是并发的意思,QtConcurrent是一个命名空间,提供了一些高级的 API,使得在编写多线程的时候,无需使用低级线程原语,如读写锁,等待条件或信号。使用QtConcurrent编写的程序会根据可用的处理器内核数自动调整使用的线程数。这意味着今后编写的应用程序将在未来部署在多核系统上时继续扩展。

QtConcurrent::run能够方便快捷的将任务丢到子线程中去执行,无需继承任何类,也不需要重写函数,使用非常简单。

QtConcurrent常用接口:

  QFuture<T> QtConcurrent::run(Function function, ...)

  QFuture<T> QtConcurrent::run(QThreadPool *pool, Function function, ...)

需要在pro文件中添加:

QT      += concurrent
 1 //main.cpp
 2 #include 
 3 #include 
 4 #include 
 5 #include 
 6 #include 
 7 #include 
 8 
 9 static QThreadPool* g_pThreadPool = QThreadPool::globalInstance();
10 
11 class HELLO
12 {
13 public:
14     QString hello(QString szName)
15     {
16         qDebug() << "Hello " << szName << " from " << QThread::currentThreadId();
17         return szName;
18     }
19 
20     void run()
21     {
22         QFuture f3 = QtConcurrent::run(this, &HELLO::hello, QString("Lily"));
23         QFuture f4 = QtConcurrent::run(g_pThreadPool, this, &HELLO::hello, QString("Sam"));
24 
25         f3.waitForFinished();
26         f4.waitForFinished();
27 
28         qDebug() << "f3 : " << f3.result();
29         qDebug() << "f4 : " << f4.result();
30     }
31 };
32 
33 QString hello(QString szName)
34 {
35     qDebug() << "Hello " << szName << " from " << QThread::currentThreadId();
36     return szName;
37 }
38 
39 int main(int argc, char *argv[])
40 {
41     QCoreApplication a(argc, argv);
42 
43     QFuture f1 = QtConcurrent::run(hello, QString("Alice"));
44     QFuture f2 = QtConcurrent::run(g_pThreadPool, hello, QString("Bob"));
45 
46     f1.waitForFinished();
47     f2.waitForFinished();
48 
49     qDebug() << "f1 : " << f1.result();
50     qDebug() << "f2 : " << f2.result();
51 
52     HELLO h;
53     h.run();
54 
55     g_pThreadPool = NULL;
56 
57     return a.exec();
58 }

展开阅读全文

页面更新:2024-04-05

标签:子类   重写   开销   线程   函数   信号   接口   内存   常用   方式   方法

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top