logo头像

技术引领生活!

Qt中的多线程

本文于821天之前发表,文中内容可能已经过时。

众所周知Qt的多线程有两种方法(写起来有三种), 注意当线程存在死循环时(即使是waitcondition等待),则该线程的事件循环将会被阻塞

  1. 继承QThread
    官方范例里面都没有说线程等待某一个事件的问题,查找文档之后发现有个QWaitCondition 很好用, 大概如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#ifndef INHERITQTHREAD_H
#define INHERITQTHREAD_H

#include <QThread>
#include <QWaitCondition>
#include <QMutex>
#include <QDateTime>
#include <QTimer>
#include <QDebug>

class InheritQThread : public QThread
{
Q_OBJECT
public:
explicit InheritQThread(QObject *parent = 0);
~InheritQThread();

signals:

public slots:

private:
QWaitCondition m_condition;
QMutex m_mutex;
bool m_quit;

protected:
void run();
};

#endif // INHERITQTHREAD_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include "inheritqthread.h"


InheritQThread::InheritQThread(QObject *parent) : QThread(parent)
{
m_quit = false;

QTimer *t = new QTimer(this);

t->setTimerType(Qt::PreciseTimer);
t->setInterval(1000);

connect(t, &QTimer::timeout, [=](){
//此段代码运行在老线程中...
m_condition.wakeAll();
});

t->start();

}

InheritQThread::~InheritQThread()
{
m_condition.wakeAll();
m_quit = true;

quit();
wait();

qDebug() << "线程退出完毕";
}

void InheritQThread::run()
{
while(true){
if(m_quit){
break;
}

m_mutex.lock();
m_condition.wait(&m_mutex);
m_mutex.unlock();

//simulate time consuming operator
for(int i = 0; i < 100000; i++){

}

qDebug() << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz");
}
}
  1. 用一个父类包裹下QThread, 分两种情况
  • 使用Controller 和 Worker 然后通过信号槽来工作, 参见官方文档的例子
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    class Worker : public QObject
    {
    Q_OBJECT

    public slots:
    void doWork(const QString &parameter) {
    QString result;
    /* ... here is the expensive or blocking operation ... */
    emit resultReady(result);
    }

    signals:
    void resultReady(const QString &result);
    };

    class Controller : public QObject
    {
    Q_OBJECT
    QThread workerThread;
    public:
    Controller() {
    Worker *worker = new Worker;
    worker->moveToThread(&workerThread);
    connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
    connect(this, &Controller::operate, worker, &Worker::doWork);
    connect(worker, &Worker::resultReady, this, &Controller::handleResults);
    workerThread.start();
    }
    ~Controller() {
    workerThread.quit();
    workerThread.wait();
    }
    public slots:
    void handleResults(const QString &);
    signals:
    void operate(const QString &);
    };
  • 将父类moveToThread,大概如下

    调用时注意 ThreadContainer *c = new ThreadContainer; 不能有父对象
    同时注意析构时删除 c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#ifndef THREADCONTAINER_H
#define THREADCONTAINER_H

#include <QObject>
#include <QThread>
#include <QTimer>
#include <QMutex>

class ThreadContainer : public QObject
{
Q_OBJECT
public:
explicit ThreadContainer(QObject *parent = 0);
~ThreadContainer();

signals:

public slots:

private:
QMutex m_mutex;
bool m_quit;

QThread *m_thread;
QTimer *m_timer;


public:
void doWork();
};

#endif // THREADCONTAINER_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include "threadcontainer.h"
#include <QDateTime>
#include <QDebug>

ThreadContainer::ThreadContainer(QObject *parent) : QObject(parent)
{
m_quit = false;

m_thread = new QThread;
m_thread->start();

//这一句
moveToThread(m_thread);

m_timer = new QTimer;
m_timer->setTimerType(Qt::PreciseTimer);
m_timer->setInterval(1000);

//注意此时doWork运行在新线程中
//connect(m_timer, &QTimer::timeout, this, &ThreadContainer::doWork);
connect(m_timer, &QTimer::timeout, this, [=](){
//不信你可以打印当前的 QThread::currentThreadId()
doWork();
});
m_timer->start();

qDebug() << "ThreadContainer" << QThread::currentThreadId();

//如果doWork是一个死循环,则这里不能直接调用,因为还是运行在父线程中,想要调用可使用以下方法,同时在doWork如果使用waitcondition,则应该写一个函数显示调用wakeAll
//1. 通过QTimer::singleShot
//2. 使用QMetaObject::invokeMethod(this, "doWork", Qt::QueuedConnection);

//此句话以后(构造函数结束)该类的函数都将运行在子线程,除非是生成该对象的线程显式的调用时,被调用的函数才会运行在调用者的线程
//既函数运行线程由调用者决定因为构造函数运行在调用者线程
}

ThreadContainer::~ThreadContainer()
{
//这段代码运行在父线程,因为析构一般都是外部调用
m_mutex.lock();
m_quit = true;
m_mutex.unlock();

m_thread->quit();
m_thread->wait();
delete m_thread;
delete m_timer;

qDebug() << "线程退出完毕";
}

//如果此函数使用while死循环,则需要外部驱动来触发等待条件
void ThreadContainer::doWork()
{
QMutexLocker locker(&m_mutex);
if(m_quit){
return;
}

//simulate time consuming operator
for(int i = 0; i < 100000; i++){

}

qDebug() << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz") << QThread::currentThreadId();
}
支付宝打赏 微信打赏

您的支持是我前行的动力!