QThread类的使用

概述
QThread类提供了一个与平台无关的管理线程的方法。一个QThread对象管理一个线程。QThread的执行从run()函数的执行开始,在Qt自带的QThread类中,run()函数通过调用exec()函数来启动事件循环机制,并且在线程内部处理Qt的事件。在Qt中建立线程的主要目的就是为了用线程来处理那些耗时的后台操作,从而让主界面能及时响应用户的请求操作。
QThread的使用方法:
1.继承QThread类:
重写run函数;
run函数是线程执行的开始,run函数中创建的类对象是在子线程中。一段简单的代码展示,run函数与main函数不在同一个线程。
Myclass.h文件:

#ifndef MYCLASS_H
#define MYCLASS_H

#include <QObject>
#include <QThread>

class MyClass : public QThread
{
    Q_OBJECT
public:
    explicit MyClass(QObject *parent = 0);
    virtual void run();
signals:
    void myThreadSignal(const int);
};

#endif // MYCLASS_H

MyClass.cpp文件:

#include "myclass.h"
#include <QDebug>

MyClass::MyClass(QObject *parent) : QThread(parent)
{
  qDebug()<<"MyClass:"<<QThread::currentThreadId();
}

void MyClass::run()
{
    qDebug()<<"myThread run() start to execute";
    qDebug()<<"current thread ID:"<<QThread::currentThreadId()<<'\n';
     int count = 0;
        for(int i = 0;i!=1000000;++i)
        {
         ++count;
        }
        //发送结束信号
        emit myThreadSignal(count);
        exec();
}

main.cpp文件:

#include <QCoreApplication>
#include "myclass.h"
#include <QThread>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    qDebug() <<"main:"<< QThread::currentThreadId();
    MyClass c;
    c.start();

    return a.exec();
}

执行结果:
这里写图片描述
结果证明main函数与run函数不在一个线程中。Myclass在主线程中,所以得出的结论:继承自QThread类的对象的创建,与run函数的执行不属于同一个线程。
2.connect函数的连接方式
直接连接:Qt::Qt::DirectConnection
发出信号所在的线程,如果与接收对线所在的线程不属于同一线程,采用直接连接的方式,槽函数会在发出信号所在的线程执行。
举例说明:
MyClass类中增加信号函数:
signals:
void myThreadSignal(const int);
新增controller类
contoller.h文件:

#ifndef CONTROLLER_H
#define CONTROLLER_H

#include <QObject>

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

signals:

public slots:
    void handleResults();
};

#endif // CONTROLLER_H

controller.cpp文件:

#include "controller.h"
#include <QDebug>
#include <QThread>

controller::controller(QObject *parent) : QObject(parent)
{
  qDebug()<<"controller construct:"<<QThread::currentThreadId();
}

void controller::handleResults()
{
    qDebug()<<"controller slots:"<<QThread::currentThreadId();


}

main.cpp文件:

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    qDebug() << "main:"<<QThread::currentThreadId();
    MyClass c;
    controller l;
    QObject::connect(&c,SIGNAL(myThreadSignal(int)),&l,SLOT(handleResults()));
    c.start();

    return a.exec();
}

代码分析:
l对象在主线程中,c对象在主线程中,但是c对象继承了QThread,在run重写中,发送的myThreadSignal信号,run函数中都是在子线程中执行的(以上都有验证),所以信号的发送所在线程与信号的接受者不在同一个线程中,信号连接采用的是直接连接的方式,所以槽函数是在子线程中执行的。
注意:在这提一点,c对象的创建是在主线程中,但是run函数中调用了c对象发送信号,这里就存在一个跨线程调用的问题。qt对I/o管理比较苛刻,禁止跨线程调用,想socket类等,都是禁止跨线程调用的,程序会报错,停止运行。大家在以后写程序中注意这一点,本文自己定义的类,所以不存在跨线程调用报错的问题。
运行结果:
这里写图片描述
Qt::QueuedConnection(队列方式)(此时信号被塞到事件队列里,信号与槽函数关系类似于消息通信,异步执行)
当信号发出后,排队到信号队列中,需等到接收对象所属线程的事件循环取得控制权时才取得该信号,调用相应的槽函数。
Qt::AutoConnection(自动方式)
信号连接的默认方式就是自动连接(缺省参数)。
Qt的默认连接方式,如果信号的发出和接收信号的对象同属一个线程,那个工作方式与直连方式相同;否则工作方式与队列方式相同。
如果信号在接收者所依附的线程内发射,则等同于直接连接
如果发射信号的线程和接受者所依附的线程不同,则等同于队列连接
以上是关于线程相关的知识,其他两中连接方式可以自行验证。

2.QObject::moveToThread()方法
注意:自己定义的类一定要继承QObject类同样在这里,写一个例子来演示一下:
work.h:

#ifndef WORKER_H
#define WORKER_H
#include <QObject>
class worker : public QObject
{
    Q_OBJECT
public:
    explicit worker(QObject *parent = 0);
signals:
    void resultReady(const int result);
public slots:
    void doWork(int parameter);
};
#endif // WORKER_H

work.cpp

#include "worker.h"
#include<QDebug>
#include <QThread>
worker::worker(QObject *parent) : QObject(parent)
{
}
void worker::doWork(int parameter)
{
            qDebug()<<"doWork:"<<QThread::currentThreadId();
           //循环一百万次
           for(int i = 0;i!=1000000;++i)
           {
            ++parameter;
           }
           //发送结束信号
           emit resultReady(parameter);
}

controller.h

#ifndef CONTROLLER_H
#define CONTROLLER_H
#include <QObject>
class controller : public QObject
{
    Q_OBJECT
public:
    explicit controller(QObject *parent = 0);
signals:
    void operate(const int);
public slots:
    void handleResults(const int value);
};
#endif // CONTROLLER_H

controller.cpp:

#include "controller.h"
#include <QDebug>
#include <QThread>

controller::controller(QObject *parent) : QObject(parent)
{
  qDebug()<<"controller construct:"<<QThread::currentThreadId();
}
void controller::handleResults(int value)
{
    qDebug()<<"controller slots:"<<QThread::currentThreadId();
    emit operate(value);
}

main.cpp

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    qDebug() << "main:"<<QThread::currentThreadId();
    worker w;
    QThread thread;
    w.moveToThread(&thread);
    controller c;
    QObject::connect(&c,SIGNAL(operate(int)),&w,SLOT(doWork(int)));
    thread.start();
    c.handleResults(0);
    return a.exec();
}

代码分析:对象w和对象c的创建都是在主线程中的,对象c的信号发射也是在主线程中,但是槽函数的执行确是在子线程中,所以证明movetothread函数是将槽函数的执行放到了子线程中。
执行结果如下:
这里写图片描述

注意一点就是,connect的连接方式,若采用直接连接的方式,槽函数的执行与信号发送者所在的线程有关。
movetothread方法将worker对象的事件循环全部交由QThread对象处理。

3.runnable的方法:
继承自QObject和QRunnable,QThreadPool;
这里也写个例子举例说明一下:
runnable.h:

#ifndef RUNNABLE_H
#define RUNNABLE_H

#include <QObject>
#include <QRunnable>
class Runnable : public QObject,public QRunnable
{
    Q_OBJECT
public:
     Runnable();
     void run();

signals:

public slots:
};

#endif // RUNNABLE_H

runnable.cpp

#include "runnable.h"
#include <QDebug>
#include <QThread>

Runnable::Runnable()
{

}

void Runnable::run()
{
    qDebug() << "child thread id: " << QThread::currentThreadId();
    int i = 10;
    while(i--)
    {
        qDebug() << QString("hello world %1").arg(i);
    }

}

main.cpp

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    qDebug() << "main:"<<QThread::currentThreadId();
    Runnable *r = new Runnable();
    QThreadPool::globalInstance()->start(r);
    //QThreadPool::globalInstance()->waitForDone();
    qDebug() << "end";
    return a.exec();
}

代码分析:
run函数部分在子线程中运行,在这里测试这段程序的时候遇到一个小插曲,执行程序的时候总是报一个错误:
error: ‘staticMetaObject’ is not a member of ‘QRunnable’,反复几次也不得其解,后来上网搜给出的答案是继承的顺序问题,大家如果遇到此问题时,可以查看一下自己写的类继承其他类顺序是否有问题。(可能我在创建时,先继承了QObject类,添加时只能接着在其后面添加继承,以前没有意识到这也有先后顺序)。

猜你喜欢

转载自blog.csdn.net/ninglu1989/article/details/82427933