Qt浅谈之二十九Qt多线程

一、简介

        Qt多线程多用于解决用户界面调用一个比较耗时的操作而导致界面冻结的问题。在此从实际的开发中总结多线程的应用。

二、详解

1、查看进程中的线程数量

 

(1)cat /proc/${pid}/status

 

VmSwap:	       0 kB
Threads:	3
SigQ:	0/29693

(2)pstree -p ${pid}

[aoyang@localhost build-threadbug-Debug]$ pstree -p 5373
threadrun(5373)─┬─{threadrun}(5375)
                └─{threadrun}(5376)

(3)top -p ${pid}再按H(或者直接输入 top -bH -d 3 -p ${pid})

 

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND            
 5373 aoyang    20   0  516m  24m  16m S  0.0  0.7   0:00.10 threadrun          
 5375 aoyang    20   0  516m  24m  16m S  0.0  0.7   0:00.00 threadrun          
 5376 aoyang    20   0  516m  24m  16m S  0.0  0.7   0:00.00 threadrun

2、QThread的run线程代码

 

(1)threadrun.h

 

#ifndef THREADRUN_H
#define THREADRUN_H
#include <QThread>
#include <QDebug>
#include <QEvent>
#include <QTimer>

class ThreadRun : public QThread                   //标准线程
{
    Q_OBJECT
public:
    ThreadRun(QObject* parent = NULL) : QThread(parent) {}
signals:
    void sigBugsignal();
public:
    void run()
    {
        while(true) {
            qDebug() << "##########thread#########" << QThread::currentThreadId();
            qDebug("----------sig-------------");
            emit sigBugsignal();
            qDebug("--------sig-end----------");
            qDebug() << "#######################";
            usleep(2000000);
        }
    }
};
#define NODEEVENT   QEvent::User+7
class HeartbeatTimerEvent :public QEvent            //自定义信号的应用
{
public:
    HeartbeatTimerEvent ()
        : QEvent(Type(NODEEVENT)) {}

    enum Op { START, STOP};
    void setOperation(enum Op op) { _op = op; }
    Op operation() { return _op; }

private:
    Op _op;
};

class HeartbeatTimer :public QTimer
{
    Q_OBJECT
public:
    HeartbeatTimer(){
        connect(this,SIGNAL(timeout()),this, SLOT(onlive()));
    }
private slots:
    void onlive(){qDebug() << "++++LIVE+++" << QThread::currentThreadId();}

protected:
    void  customEvent(QEvent *event)
    {
        if (event->type() == QEvent::Type(NODEEVENT)) {
            HeartbeatTimerEvent* ne = dynamic_cast<HeartbeatTimerEvent*>(event);
            if (ne->operation() == HeartbeatTimerEvent::START)
                this->start();
            else if (ne->operation() == HeartbeatTimerEvent::STOP)
                this->stop();
        }
    }
};
#endif // THREADRUN_H

(2)threadmain.h

#ifndef THREADMAIN_H
#define THREADMAIN_H
#include <QDebug>
#include <QMainWindow>
#include <QDebug>
#include <QMutex>
#include "threadrun.h"

namespace Ui {
class Widget;
}

class Runs : public QMainWindow
{
    Q_OBJECT

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

private slots:
    void onBugSlot();
    void onLive();

private:
    Ui::Widget *ui;
    QMutex mutex;
    ThreadRun* bt;
    QThread *heartThread;
    HeartbeatTimer *heartTimer;
};

#endif // THREADMAIN_H

(3)threadmain.cpp

 

#include <QTimer>
#include "threadmain.h"
#include "ui_widget.h"
#include "threadrun.h"

Runs::Runs(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    qDebug() << "-------start-------" << QThread::currentThreadId();
    bt = new ThreadRun(this);
    connect(bt, SIGNAL(sigBugsignal()), this, SLOT(onBugSlot()));
    bt->start();

    heartThread = new QThread(this);
    heartTimer = new HeartbeatTimer();           //定义器单独运行在另一个线程中
    heartTimer->setInterval(2000);
    //heartTimer->start();                        //位置要正确,否则定时器无法start
    heartTimer->moveToThread(heartThread);
    connect(heartTimer, SIGNAL(timeout()), this, SLOT(onLive()));
    HeartbeatTimerEvent * ev = new HeartbeatTimerEvent;     //自定义事件,可以不使用直接使用heartTimer->start();
    ev->setOperation(HeartbeatTimerEvent::START);
    qApp->postEvent(heartTimer, ev);

    heartThread->start();
    qDebug() << "*****************" <<QThread::idealThreadCount();
}

Runs::~Runs()
{
    delete ui;
    if (bt->isRunning()) {
        bt->terminate();
        bt->wait();
    }
    if (heartThread->isRunning()) {
        heartThread->exit();
        heartThread->wait();
    }
}

void Runs::onBugSlot()
{
    mutex.lock();
    qDebug() << "------slot------" << QThread::currentThreadId();
    //sleep(0.5);
    //QApplication::processEvents();
    mutex.unlock();
}

void Runs::onLive()
{
    qDebug() << "------QTimer------" << QThread::currentThreadId();
}

(4)运行结果

       其中ThreadRun的run函数是单独运行在一个线程中,ThreadRun的构造函数和其他的接口函数及其连接的槽函数,都是运行在主线程中;moveToThread执行start后的函数调用才是运行在另一个线程中,其他的timeout()连接的槽还是运行在主函数中。

 

3、QThread的moveToThread线程代码

(1)mainwindow.h

 

 

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include<QTimer>
#include <QDebug>
#include<QThread>

#include "firstthread.h"
#include "secondthread.h"


namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT
    
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
    
private:
    Ui::MainWindow *ui;

    FirstThread firstObj;
    SecondThread secondObj;

    QThread firstThread;
    QThread secondThread;
    QTimer  time;

private slots:
    void slotTimeOut();

signals:
    void output(int);
};

#endif // MAINWINDOW_H

(2)mainwindow.cpp

 

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QTime>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    connect(this,SIGNAL(output(int)),&firstObj,SLOT(input(int)));
    connect(&firstObj,SIGNAL(output(int)),&secondObj,SLOT(input(int)));

    firstObj.moveToThread(&firstThread);
    secondObj.moveToThread(&secondThread);
    firstThread.start();
    secondThread.start();

    connect(&time,SIGNAL(timeout()),this,SLOT(slotTimeOut()));
    time.setInterval(5000);
    time.start();

    ui->label->setText(QString::number(this->thread()->currentThreadId()));
//    ui->label->setText(QString(secondObj.thread()->currentThreadId()));
//    ui->label->setText(QString(MainWindow.thread()->currentThreadId()));
//    ui->label->setText(QString(firstThread.thread()->currentThreadId()));
//    ui->label->setText(QString(secondThread.thread()->currentThreadId()));

    qDebug() << "***********************";
    qDebug()<< "***main:" << QThread::currentThreadId();
    qDebug()<< "***firstThread:" <<firstThread.currentThreadId();
    qDebug()<< "***firstObj:" <<firstObj.thread()->currentThreadId();
    qDebug()<< "***secondThread:" <<secondThread.currentThreadId();
    qDebug()<< "***secondObj:" <<secondObj.thread()->currentThreadId();
    qDebug() << "***********************";
}

MainWindow::~MainWindow()
{
    time.stop();
    firstThread.quit();
    secondThread.quit();
    if(firstThread.isRunning()) firstThread.wait();
    if(secondThread.isRunning()) secondThread.wait();
    delete ui;
}
void MainWindow::slotTimeOut()
{
    qDebug() << "##############################";
    QTime tm;
    tm = QTime::currentTime();
    qsrand(tm.msec()+tm.second()*1000);
    int num =qrand()> 6000 ? qrand()%9 : -qrand()%(9);
    emit output(num);
    qDebug()<<"===============QTimer:"<< num;
    qDebug()<<"===============QTimer:" << QThread::currentThreadId();
}

(3)firstthread.h

 

#ifndef FIRST_THREAD_H
#define FIRST_THREAD_H

#include <QObject>
#include<QThread>
#include<QDebug>

class FirstThread : public QObject
{
    Q_OBJECT
public:
    explicit FirstThread(QObject *parent = 0);
private:
    int value;
signals:
    void output(int);
    void outputmy();
    
public slots:
    void input(int value);
    void inputmy();
    
};

#endif // ATHREAD_H

(4)firstthread.cpp

 

#include "firstthread.h"

FirstThread::FirstThread(QObject *parent) :
    QObject(parent)
{
    qDebug() << "---------FirshThread---------" << QThread::currentThreadId();
    connect(this,SIGNAL(outputmy()),this,SLOT(inputmy()));
}
void FirstThread::input(int value)
{
     int value1 = value;
     emit outputmy();
     emit output(value1);

     qDebug() << QString("---------void FirstThread::input(%1)").arg(value) << QThread::currentThreadId();
}
void FirstThread::inputmy()
{
     qDebug() << "--------void FirstThread::inputmy() " << QThread::currentThreadId();
}

(5)secondthread.h

 

#ifndef SECOND_THREAD_H
#define SECOND_THREAD_H

#include <QObject>
#include<QDebug>
#include<QThread>

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

private:
    int value;
    
signals:
//    void output(int);
    
public slots:
    void input(int val);
};

#endif // SECOND_THREAD_H

(6)secondthread.

 

#include "secondthread.h"

SecondThread::SecondThread(QObject *parent)
    : QObject(parent)
{
    qDebug() << "+++++++++SecondThread+++++++++" << QThread::currentThreadId();

}
void SecondThread::input(int val)
{
    int value = val;
//    emit output(value);
    qDebug() << QString("+++++++++SecondThread::input(%1)").arg(value) << QThread::currentThreadId();
}

(7)运行结果

 

 

          其中定时器及其槽函数属于主线程,firstObj类和secondObj的构造函数主函数中运行,只有当QThread执行start后,其槽函数才在各自的线程中运行。

4、Qt信号量QSemaphore

        QMutex互斥量只能锁定一次而信号量QSemaphore可以获取多次,它可以用来保护一定数量的同种资源。最重要的是QSemaphore(1)可以控制不同线程间的相同资源,可以应用于程序的阻塞和运行。
(1)mythread.h       

#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QtCore>

class MyThread  : public QThread
{
  Q_OBJECT
public:
    MyThread();
    void run();
    void stop();
    QString name;
    bool stopflag;
    //QReadWriteLock  lock ;
    QSemaphore *sem;
    QTimer *time;
    
private slots:
    void slotTimeOut();
};

#endif // MYTHREAD_H

(2)mythread.cpp  

#include "mythread.h"

MyThread::MyThread()
{
  stopflag = false;
  time = new QTimer(this);
  connect(time, SIGNAL(timeout()), this, SLOT(slotTimeOut()));
  time->setInterval(1000);
  time->start();
  sem = new QSemaphore(1);
}

void MyThread::run()
{
  while(true) {
     //lock.lockForRead();
    sem->acquire();
    if (stopflag) break;
    qDebug() << this->name << "------run...";
  }
  qDebug() << this->name << "------stop...";
}

void MyThread::slotTimeOut()
{
  sem->release();
  //lock.unlock();
}

void MyThread::stop()
{
  if (time->isActive()) {
    time->stop();
  }
  stopflag = true;
  sem->release();
}
 

(3)运行结果

       QSemaphore与QTimer一起应用,可以防止while死循环一直运行(也可加sleep睡眠)。若在生产者和消费者模型中就不用使用QTimer了。

四、总结

(1)子类化QThread中并重新实现的run和通过moveToThread将类移动到线程是Qt线程的两种不同方式,可根据需要选择。
(2)其他线程的知识点可参看其他文档,如线程的互斥、同步及各线程间的通信。
(3)源码已经打包上传到csdn上,可登录下载(http://download.csdn.net/detail/taiyang1987912/9463972)。
(4)若有建议,请留言,在此先感谢! 

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乌托邦2号

博文不易,支持的请给予小小打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值