前一节介绍了线程的创建,把一个线程单独作为一类且是继承Qthread,当中也只有一个线程处理函数。很大的弊端。
- 规定了继承Qthread类,若要使用QWidget等其他基类呢?
- 线程处理函数run(),只能重写,不可以自定义。
因此,在Qt4.7及以后版本推荐使用以下的工作方式。其主要特点就是利用Qt的事件驱动特性,将需要在子线程中处理的业务放在独立的模块(类)中,由主线程创建完该对象后,将其移交给指定的线程,且可以将多个类似的对象移交给同一个线程。
换句话说,就是新建一个类MyThread,该类不是继承Qthread类,但必须继承QObject。将MyThread当成一个普通的类非线程类。再在窗体使用Qthread类来创建子线程对象,将类MyThread附加到子线程Qthread。(MyThread就是乘客,Qthread就是汽车)。
问题一:为什么自定义类不可以指定父对象?
void QObject::moveToThread(QThread * targetThread)
在调用moveToThread,若自定义类指定了父对象,而Qthread是有父对象的,moveToThread不允许使用。
问题二:为什么要使用signal-slot,而不直接调用线程处理函数。
直接调用,导致会导致线程处理函数和主线程是在同一个线程中。
NewThread->MySlot()
使用signal-slot来调用,主线程和子线会独立。
多线程使用过程中注意事项:
线程不能操作UI对象(从Qwidget直接或间接派生的窗口对象)
需要移动到子线程中处理的模块类,创建的对象的时候不能指定父对象。
结果图:
源代码:
自定义线程部分
newthread.h
#ifndef NEWTHREAD_H
#define NEWTHREAD_H
#include <QObject>
class NewThread : public QObject
{
Q_OBJECT
public:
explicit NewThread(QObject *parent = 0);
public:
void MySlot(); //自定义线程处理函数
signals:
void MySignal();//自定义信号
public slots:
};
#endif // NEWTHREAD_H
newthread.cpp
#include "newthread.h"
#include <QThread>
#include <QDebug>
NewThread::NewThread(QObject *parent) :
QObject(parent)
{
}
void NewThread::MySlot()
{
QThread::sleep(5);
//操作完成,发出信号
emit MySignal();
qDebug()<<"子线程ID:"<<QThread::currentThreadId();
}
主线程部分
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTimer>
#include "newthread.h"
#include <QThread>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
signals:
void threadSignal(); //提示去调用线程处理函数
private slots:
void on_pushButton_clicked(); //开始按钮
void on_pushButton_2_clicked(); //暂停按钮
private:
Ui::Widget *ui;
QTimer *timer; //定时器
NewThread *myThread; //自定义线程对象
QThread *thread; //子线程
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QThread>
#include <QDebug>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//自定义线程对象,不可以指定父对象
myThread =new NewThread;
//创建子线程
thread =new QThread(this);
//将自定义线程对象附加到子线程
myThread->moveToThread(thread);
timer=new QTimer(this);
//定时器信号
connect(timer,&QTimer::timeout,
[=]()
{
static int num=0;
ui->lcdNumber->display(num);
num++;
}
);
//调用线程处理函数
connect(this,&Widget::threadSignal,myThread,&NewThread::MySlot);
//接受子线程发出信号
connect(myThread,&NewThread::MySignal,
[=]()
{
qDebug()<<"over";
timer->stop();
}
);
connect(this,&QWidget::destroyed,
[=]()
{
thread->quit();
thread->wait();
}
);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
//开始定时器
if(timer->isActive()!=true)
timer->start(1000);
//开始线程,但不启动线程处理函数
thread->start();
//发出信号,提示去调用线程处理函数
emit threadSignal();
//myThread->MySlot();
qDebug()<<"主线程ID:"<<QThread::currentThreadId();
}
void Widget::on_pushButton_2_clicked()
{
if(timer->isActive()==true)
timer->stop();
}
补充:
关于Qobject类的connect函数最后一个参数,连接类型:
自动连接(AutoConnection),默认的连接方式。
如果信号与槽,也就是发送者与接受者在同一线程,等同于直接连接;
如果发送者与接受者处在不同线程,等同于队列连接。
直接连接(DirectConnection)
当信号发射时,槽函数立即直接调用。无论槽函数所属对象在哪个线程,槽函数总在发送者所在线程执行。
队列连接(QueuedConnection)
当控制权回到接受者所在线程的事件循环时,槽函数被调用。槽函数在接受者所在线程执行。
总结:
* 队列连接:槽函数在接受者所在线程执行。
* 直接连接:槽函数在发送者所在线程执行。
* 自动连接:二者不在同一线程时,等同于队列连接