一、简介
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)若有建议,请留言,在此先感谢!