第一种方法:
使用QObject实现多线的方法见:http://blog.youkuaiyun.com/czyt1988/article/details/71194457
1.摘要
Qt有两种多线程的方法,其中一种是继承QThread的run函数,另外一种是把一个继承于QObject的类转移到一个Thread里。
Qt4.8之前都是使用继承QThread的run这种方法,但是Qt4.8之后,Qt官方建议使用第二种方法。两种方法区别不大,用起来都比较方便,但继承QObject的方法更加灵活。这里要记录的是如何正确的创建一个线程,特别是如何正确的退出一个线程。
本文先介绍QThread的普通用法,这个用法可能网上很多文章都介绍过,如果已经了解大可跳过此节,本文重点介绍线程退出的几种方法,根据需求正确的创建和退出线程等问题。
2.Qt多线程方法1 继承QThread
在使用继承QThread
的run
方法之前需要了解一条规则:
QThread
只有run
函数是在新线程里的,其他所有函数都在QThread
生成的线程里
QThread
只有run
函数是在新线程里的
QThread
只有run
函数是在新线程里的
QThread
只有run
函数是在新线程里的
重要的事情说3遍!!!
,如果QThread
是在ui所在的线程里生成,那么QThread
的其他非run
函数都是和ui线程一样的,所以,QThread
的继承类的其他函数尽量别要有太耗时的操作,要确保所有耗时的操作都在run
函数里。
在UI线程下调用QThread
的非run
函数(其实也不应该直接调用run
函数,而应该使用start函数),和执行普通函数无区别,这时,如果这个函数要对QThread
的某个变量进行变更,而这个变量在run
函数里也会被用到,这时就需要注意加锁的问题,因为可能这个变量前几毫秒刚刚在run中调用,再调用时已经被另外的线程修改了。
2.1写一个继承于QThread
的线程
本文的重点不是教会你继承run
写一个多线程,任何有编程基础的5分钟就能学会使用QThread
的方法,本文真正要讲的是后面那几节,如如何安全的退出一个线程,如何开启一个临时线程,运行结束马上关闭等问题。如果你对QThread
有初步了解,那么可以略过这节,但你最好看看这节后面提出的几个问题。
任何继承于QThread
的线程都是通过继承QThread
的run
函数来实现多线程的,因此,必须重写QThread
的run
函数,把复杂逻辑写在QThread
的run
函数中。
看看一个普通继承QThread
的例子:
头文件:
#ifndef THREADFROMQTHREAD_H
#define THREADFROMQTHREAD_H
#include <QThread>
class ThreadFromQThread : public QThread
{
Q_OBJECT
signals:
void message(const QString& info);
void progress(int present);
public:
ThreadFromQThread(QObject* par);
~ThreadFromQThread();
void setSomething();
void getSomething();
void setRunCount(int count);
void run();
void doSomething();
private:
int m_runCount;
};
#endif // THREADFROMQTHREAD_H
cpp文件:
#include "ThreadFromQThread.h"
#include <QDebug>
ThreadFromQThread::ThreadFromQThread(QObject* par) : QThread(par)
,m_runCount(20)
{
}
ThreadFromQThread::~ThreadFromQThread()
{
qDebug() << "ThreadFromQThread::~ThreadFromQThread()";
}
void ThreadFromQThread::setSomething()
{
msleep(500);
QString str = QString("%1->%2,thread id:%3").arg(__FUNCTION__).arg(__FILE__).arg((int)QThread::currentThreadId());
emit message(str);
}
void ThreadFromQThread::getSomething()
{
msleep(500);
emit message(QString("%1->%2,thread id:%3").arg(__FUNCTION__).arg(__FILE__).arg((int)QThread::currentThreadId()));
}
void ThreadFromQThread::setRunCount(int count)
{
m_runCount = count;
emit message(QString("%1->%2,thread id:%3").arg(__FUNCTION__).arg(__FILE__).arg((int)QThread::currentThreadId()));
}
void ThreadFromQThread::run()
{
int count = 0;
QString str = QString("%1->%2,thread id:%3").arg(__FILE__).arg(__FUNCTION__).arg((int)QThread::currentThreadId());
emit message(str);
while(1)
{
sleep(1);
++count;
emit progress(((float)count / m_runCount) * 100);
emit message(QString("ThreadFromQThread::run times:%1").arg(count));
doSomething();
if(m_runCount == count)
{
break;
}
}
}
void ThreadFromQThread::doSomething()
{
msleep(500);
emit message(QString("%1->%2,thread id:%3").arg(__FUNCTION__).arg(__FILE__).arg((int)QThread::currentThreadId()));
}
这个简单的例子有一个Qt类常见的内容,包含了普通方法,信号槽,和一个run
函数。这里函数setSomething();
进行了500ms的延迟,getSomething
同理。这是为了验证在QThread::run()
之外调用QThread成员函数不会运行在新线程里。
上面代码用到了QThread::currentThreadId()
这是一个静态函数,用于返回当前线程句柄,这个值除了区分以外没有别的用处。
为了验证这个线程,编写一个简单的界面,这个界面主要用于验证如下几个问题:、
- 在UI线程调用setSomething();
函数和getSomething();
函数会不会卡顿?
- 在UI线程调用QThread::quit()
或QThread::exit()
函数会不会停止线程?
- 在UI线程调用QThread::terminate
函数会不会停止线程?
-如何正确的退出线程?
2.2 QThread的几个函数quit、exit、terminate函数
为了验证上面这些,编写一个简单的界面如下图所示:
#include "Widget.h"
#include "ui_Widget.h"
#include "ThreadFromQThread.h"
#include <QDebug>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//控件初始化
ui->progressBar->setRange(0,100);
ui->progressBar->setValue(0);
ui->progressBar_heart->setRange(0,100);
ui->progressBar_heart->setValue(0);
//按钮的信号槽关联
connect(ui->pushButton_qthread1,&QPushButton::clicked
,this,&Widget::onButtonQThreadClicked);
connect(ui->pushButton_qthread1_setSomething,&QPushButton::clicked
,this,&Widget::onButtonQthread1SetSomethingClicked);
connect(ui->pushButton_qthread1_getSomething,&QPushButton::clicked
,this,&Widget::onButtonQthread1GetSomethingClicked);
connect(ui->pushButton_qthreadQuit,&QPushButton::clicked
,this,&Widget::onButtonQthreadQuitClicked);
connect(ui->pushButton_qthreadTerminate,&QPushButton::clicked
,this,&Widget::onButtonQthreadTerminateClicked);
connect(ui->pushButton_qthreadExit,&QPushButton::clicked
,this,&Widget::onButtonQThreadExitClicked);
connect(ui->pushButton_doSomthing,&QPushButton::clicked
,this,&Widget::onButtonQThreadDoSomthingClicked);
connect(ui->pushButton_qthreadRunLocal,&QPushButton::clicked
,this,&Widget::onButtonQThreadRunLoaclClicked);
//
connect(ui->pushButton_qobjectStart,&QPushButton::clicked
,this,&Widget::onButtonObjectMove2ThreadClicked);
connect(ui->pushButton_objQuit,&QPushButton::clicked
,this,&Widget::onButtonObjectQuitClic