QT线程的使用(一)

为什么用线程

在使用QT做窗体程序时有一些占用时间较长的函数在运行时会使QT的窗体控件无法得到响应,也就是常说的程序假死,其实程序还是在运行的,只是你得不到反馈而已,这种情况,可以使用线程来把运行时间长的函数与窗体主线成分开了,当然也可以使用QTimer来执行这一个操作,但QTimer与线程相比有些缺陷,当然,使用线程也有一些缺陷,这些后边的文章再说明。

线程类的创建

QT线程类有两种创建方式,一种是继承QThread的方式,第二种是继承QObjec的方式,本文是基于第二种方式来创建线程的(QT推荐的方式)以下是线程类的代码:

class testThread : public QObject
{   
    Q_OBJECT
    public:
        explicit testThread(QObject *parent = nullptr);    
    signals:
        void send_msg(const QString& msg);
    public slots:
        void ChildThread();              //  子线程主循环
        void Stop();                     //  改变事件循环的状态(停止)
        void Start();                    //  改变事件循环的状态(开始)
    private:
        volatile bool isStop = false;    // 改变事件循环的标志位
        int n = 0;
};
void testThread::ChildThread()
{
    while(!isStop) {                        // 判断是否停止循环
        QString msg = "child: " + QString::number(n++);
        emit send_msg(msg);                // 发送信号,窗体显示信息
        QThread::msleep(50);               // 输出一次消息休息50ms
    }
    qDebug() << "child Thread: " << QThread::currentThread();
}
void testThread::Start()
{
    isStop = false;
}
void testThread::Stop()
{
    isStop = true;
}

使用线程

QObject类的方法要想在线程中执行,还涉及到了另一个知识即connect的链接方式,在我理解中,因为connect的存在而使得使用QT的线程非常方便 ,通过不同的连接方式可以很方便的判断出该类的曹函数是在哪一个线程中执行的,先把代码贴上,看一下怎么使用线程,具体的看代码注释:


// mainwindow的头文件,可掠过这段代码
class MainWindow : public QMainWindow
{
    Q_OBJECT

    public:
        explicit MainWindow(QWidget *parent = 0);
        ~MainWindow();

    signals:
        void Stop();
        void Start();

    private:
        Ui::MainWindow *ui;
        QTimer timer;
        QThread *qThread;                  // 声明一个QThread线程管理类用来管理testThread
        testThread *childThread;           // 声明一个testThread类,把它扔到线程中
        int n = 0;
    private slots:
        void bt_start();
        void bt_stop();
        void receive_msg(const QString& msg);     // 接收子线程函数发来的信号并把信息显示在对话框中
        void mainMsg();                           // 主线程显示的信息 
};

以下代码是本文重点了,讲述了如何把一个我们建立的testThread类的函数放入线程中执行

// 重点
void MainWindow::bt_start()
{
    if( !timer.isActive() ) {
        timer.setInterval(50);
        timer.start();
    }
    if( childThread == NULL ) {                    // 判断对象有无申请空间, 如果已经创建了对象,则
        childThread = new testThread();            // 不再重复创建
        qThread   = new QThread();                 //
        childThread->moveToThread(qThread);        // 将testThread类丢入QThread中进行管理(重要)
    }

    connect(this, &MainWindow::Stop, childThread, &testThread::\
            Stop, Qt::DirectConnection);            // 第五个参数很重要,决定了曹函数在哪个线程执行
    connect(childThread, &testThread::send_msg, this, &MainWindow::\
            receive_msg);
    connect(&timer, &QTimer::timeout, this, &MainWindow::\
            mainMsg);
    connect(this, &MainWindow::Start, childThread, &testThread::\
            ChildThread);                          // 连接信号,开启线程的主循环函数
    connect(this, &MainWindow::Start, childThread, &testThread::\
            Start);                                // 改变子线程循环标志位

    if( !qThread->isRunning() ) {
        qThread->start();    // 开启管理线程,加入判断是为了避免线程的重复开启
    }
    emit Start();            // 发送开启信号
    }

在上面的代码中打了注释的地方描述了Qt中一个线程创建的过程:

  1. 初始化QObject的子类即自己创建的testThread类
    childThread = new testThread();
  2. 初始化QThread类
    qThread = new QThread();
  3. 将QObject的子类moveToThread到刚刚初始化的QThread中进行管理
    childThread->moveToThread(qThread);
  4. 使用connect发送信号到QObject子类的对象中执行函数
  5. 开启管理线程
    qThread->start();

到现在,线程已经可以运行了,下面则是停止线程了,在本文中我说的停止线程并没有将线程关闭,而是类似于挂起的状态,等到再次发送信号时,线程的执行函数还将继续运行且之前的资源并没有被释放掉,在C语言中线程执行的函数如果执行结束,如果线程的引用数为0那么该线程也将关闭,并且除了小部分资源(例如返回码的所占的内存)其他用户自定义的线程的资源将被释放,在QT中因为封装好了线程类,所以我们看不到这里面的状态,第二篇文章我会说一下该怎么在QT中正确的关闭线程。

void MainWindow::bt_stop()
{
    if( timer.isActive() ) {
        timer.stop();
    }
    emit Stop();    // 发送停止信号,改变循环标志位
    disconnect(childThread, &testThread::send_msg, this, &MainWindow::\
            receive_msg);    // 断开线程循环函数中的信号连接
    disconnect(&timer, &QTimer::timeout, this, &MainWindow::\
            mainMsg);       
    disconnect(this, &MainWindow::Start, childThread, &testThread::\
            ChildThread);    // 断开执行函数信号的连接
    disconnect(this, &MainWindow::Start, childThread, &testThread::\
            Start);          // 断开开始标志位信号的连接
    qDebug() << "main thread: " << QThread::currentThread();  // 显示当前线程
}

下面是运行程序时显示的画面
这里写图片描述

图中可以看到,程序运行之后输出的信息是在不同线程中的,至此,本文结束,有疑问的朋友可以私信评论,有什么说得不到位的或者错误的也可以提出来

你可能感兴趣的:(Qt)