本文源码: QT多线程实战
窗口本身就是一个死循环,在这样一个死循环中执行任何耗时的操作,都会导致程序崩溃。所以多线程对于窗口编程而言是必要的。
例如,在窗口中拖入一个pushButton
和lineEdit
,将pushButton
的名字改为pbStart
,然后为其绑定一个函数,函数中创建一个死循环,并让死循环中的内容实时输出到lineEdit
。
接下来设计一个逻辑,当点击pbStart
之后,开始执行死循环,同时按钮内容变为Stop
;当按钮内容为Stop
时,点击按钮,停止执行死循环。
所以,需要设计一个用于控制循环是否继续执行的成员变量,在MainWindow
的头文件中添加私有成员bool flag
。在cpp
文件中添加flag = true
。
然后转到设计界面 ,右键pbStart
按钮->转到槽
->clicked()
。从而MainWindow
中添加了一个名为on_pbStart_clicked
的函数,填充其内容为
void MainWindow::on_pbStart_clicked()
{
int i= 0;
bool flag = QString::compare(ui->pbStart->text(),"start");
if(flag)
ui->pbStart->setText("stop");
while(flag){
i++;
ui->lineEdit->setText(QString::number(i));
}
}
结果运行之后,果然陷入了死循环,程序也无法响应了,最过分的是lineEdit
并没有变化,这个时候就要考虑多线程。
QThread
是Qt中最基础的线程类,每个实例都可以控制一个线程。其传统的调用方式是,新建一个继承QThread
的类,然后将耗时任务写入run
函数。而自QT4.4之后,则建议通过moveToThread()
函数来调用多线程。
首先新建一个类,在项目中Ctrl
+N
,在弹出对话框中选择Files and Classes
->C/C++
->C++ Class
,定义类的名称为ThTest
,并选中QObject
。
令该类继承QObject
,将头文件中的ThTest
改为
class ThTest : public QObject
{
Q_OBJECT
public:
ThTest();
~ThTest();
void Func(void);
};
相应地,cpp
文件的内容为
#include "thtest.h"
#include <QDebug>
#include <QThread>
ThTest::ThTest(){
}
ThTest::~ThTest(){
}
void ThTest::Func()
{
int NowNum = 0;
while(true){
NowNum++;
qDebug()<<NowNum<<QThread::currentThreadId();
}
}
然后更改mainwindow
的代码,在其头文件内容如下
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QThread>
#include <QObject>
#include <QDebug>
#include "thtest.h"
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
signals:
void ToThread(); // 信号
private slots:
void on_pbStart_clicked();
private:
Ui::MainWindow *ui;
QThread *qThTest;
ThTest *th1;
};
#endif
然后将cpp
文件中的on_pbStart_clicked
函数改为
void MainWindow::on_pbStart_clicked()
{
qThTest = new QThread;
th1 = new ThTest;
connect(this,&MainWindow::ToThread,th1,&ThTest::Func);
th1->moveToThread(qThTest);
qThTest->start();
emit ToThread();
}
其中,connect
将ToThread
函数和ThTest
中的Func
函数绑定在了一起。也就是说,当这边发射ToThread
的信号的时候,Th1
会执行Func
这个函数。
所以,当线程启动后,通过emit
发射ToThread
信号,果然命令行中会持续输出数字了,同时窗口并不会死掉。
到了这一步,其实已经可以处理一些多线程任务了,但还不能把递增的NowNum
显示到主窗口上,从而让lineEdit
看上去有些鸡肋。为了实现在窗口上显示递增的数字,接下来需要做的就是线程间的通信。
得益于Qt的信号槽机制,多线程之间的通信并不复杂。乃至于,可以广义地认为emit ToThread()
本身也是一个线程间通信的过程。
所以只需在on_pbStart_clicked
中添加一条
connect(th1,SIGNAL(sendInt(int)),this,SLOT(getInt(int)));
意思就是th1
发射一个sendInt(int)
,this
接收一个getInt(int)
,这两个函数的名字无所谓,但一定不包含形参,而只有形参的数据类型。
接下来,在thtest.h
中添加sendInt
,
signals:
void sendInt(int);
并更改其Func
函数
void ThTest::Func(){
int NowNum = 0;
while(true){
QThread::sleep(1);
emit sendInt(NowNum++);
}
}
最后,在mainwindow.h
中添加
private slots:
void getInt(int);
以及cpp
文件中的
void MainWindow::getInt(int num){
ui->lineEdit->setText(QString::number(num));
}
这样,在点击start
之后,就可以看到lineEdit
上数字的变化了。
最后,回到一开始的需求,是点击start
开始,然后按钮变为stop
,点击stop
后再停止。
考虑到terminate
并不安全,故而采用终结while
的方式来退出死循环。方法是在ThTest
中添加一个bool
类型的成员running
,即在头文件中添加
public:
bool running=true;
并更改源文件中的while
循环
void ThTest::Func(){
int NowNum = 0;
while(running){
emit sendInt(NowNum++);
QThread::sleep(1);
}
}
最后修改on_pbStart_clicked
void MainWindow::on_pbStart_clicked()
{
bool flag = QString::compare(ui->pbStart->text(),"stop");
qDebug()<<ui->pbStart->text();
th1->running = flag;
ui->pbStart->setText(flag?"stop":"start");
if(flag){
th1->moveToThread(qThTest);
qThTest->start();
emit ToThread();
}
else
ui->lineEdit->setText("0");
}
并把th1
等初始化过程移除去
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
qThTest = new QThread;
th1 = new ThTest;
connect(th1,SIGNAL(sendInt(int)),this,SLOT(getInt(int)));
connect(this,&MainWindow::ToThread,th1,&ThTest::Func);
}
结果线程果然终止了
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/234553.html原文链接:https://javaforall.cn