Qt多线程(二)
上一篇通过继承QThread类,重写run()虚函数实现多线程应用程序,这一篇通过另一种方式实现Qt的多线程应用程序编程。实现功能为线程定时器的例子。
1)新建工程后,添加线程类MyThread,基类为QObject;
//MyThread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QObject>
class MyThread : public QObject
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = 0);
//线程处理函数
void myTimeout();
void setFlag(bool flag = true);
signals:
void mySignal(int *);
public slots:
private:
bool bIsStop; //控制线程的启停 为真停止
};
#endif // MYTHREAD_H
//MyThread.cpp
#include "mythread.h"
#include <QThread>
#include <QDebug>
MyThread::MyThread(QObject *parent) : QObject(parent)
{
bIsStop = false;
}
void MyThread::myTimeout()
{
static int a = 0;
while(!bIsStop)
//while(1)
{
a++;
QThread::sleep(1);
emit mySignal(&a);
qDebug()<<"子线程 id="<<QThread::currentThread();
}
}
void MyThread::setFlag(bool flag)
{
bIsStop = flag;
}
2)在主线程中启动子线程
//主线程头文件
#ifndef MYWIDGET_H
#define MYWIDGET_H
#include <QWidget>
#include "mythread.h"
#include <QThread>
namespace Ui {
class myWidget;
}
class myWidget : public QWidget
{
Q_OBJECT
public:
explicit myWidget(QWidget *parent = 0);
~myWidget();
private slots:
void on_startButton_clicked();
void on_stopButton_clicked();
void dealCloseWnd();
void dealSignal(int *);
signals:
void startThread(); //启动子线程信号,必须通过信号-槽的方式启动线程函数
private:
Ui::myWidget *ui;
MyThread *myT;
QThread *thread;
};
#endif // MYWIDGET_H
//主线程cpp
#include "mywidget.h"
#include "ui_mywidget.h"
#include <QDebug>
myWidget::myWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::myWidget)
{
ui->setupUi(this);
//1)动态分配空间,不能指定父对象
myT = new MyThread;
//2)创建子线程
thread = new QThread(this);
//3)把自定义的线程加如到子线程中
myT->moveToThread(thread);
connect(myT,SIGNAL(mySignal(int*)),this,SLOT(dealSignal(int*)));
qDebug()<<"主线程 id="<<QThread::currentThread();
//4)启动线程函数信号-槽连接
connect(this,&myWidget::startThread,myT,&MyThread::myTimeout);
connect(this,&myWidget::destroyed,this,&myWidget::dealCloseWnd);
//线程处理函数内部不允许操作图形界面
//connect的第5个参数,表示连接方式:默认是队列 ,直接
//多线程才有意义
//默认的时候,如果是多线程,默认使用队列
//如果是单线程,默认使用直接方式
//队列方式:槽函数所在的线程和接收者一样(第4个参数和第3个参数)
//直接方式:槽函数所在线程和发送者一样(第4个参数和第1个线程)
//通过判断类来判断是多线程还是单线程
}
myWidget::~myWidget()
{
delete ui;
}
void myWidget::dealCloseWnd()
{
myT->setFlag(true);
thread->quit();
thread->wait();
delete myT;
}
void myWidget::dealSignal(int *a)
{
//static int a = 0;
//a++;
ui->lcdNumber->display(*a);
}
void myWidget::on_startButton_clicked()
{
if(thread->isRunning() == true)
{
return;
}
//启动线程,但是没有启动线程处理函数
thread->start();
//不能直接调用线程处理函数
//直接调用,导致线程处理函数和主线程是在同一个线程
//myT->myTimeout();
myT->setFlag(false);
emit startThread();
}
void myWidget::on_stopButton_clicked()
{
if(thread->isRunning() == false)
{
return;
}
//通过使用flag标志控制线程停止
myT->setFlag(true);
thread->quit();
thread->wait();
}
需要注意:1)例子中通过使用flag标志在控制线程函数的启动和停止,如果不使用该标志位控制线程,当调用thread->quit()和thread->wait()后线程函数仍然在执行,原因是在执行thread->quit()和thread->wait()后,只用线程函数的任务处理完后,线程函数才会停止工作,而线程函数为死循环,所以会一直执行下去。同样道理在窗口关闭时也需要使用标志停止线程函数。
2)启动线程必须使用信号-槽方式,不能直接调用线程处理函数;
3)线程类可以定义多个函数,但是有且只能有一个线程处理函数,本例中void MyThread::myTimeout();