自己写了一个关于QThread的小练习,实现子类计数,发送信号给主界面,主界面进行显示计数,同时还可以实现计数的暂停、重新结束,点击关闭按钮进行关闭。
QThread使用继承QObject的方法实现的。doWork有两种实现形式,一种定时器,一种sleep方式,都可以。
对应的头文件
#ifndef MTTHREAD_H
#define MTTHREAD_H
#include <QObject>
#include <QTimer>
#include <QMutex>
#include <QMutexLocker>
//思路:新类继承于QObject ,开启一个定时器,初始变量m_nTimer 依次增加,并发送主界面中,实现lcd 的显示
class mtThread : public QObject
{
Q_OBJECT
public:
explicit mtThread(QObject *parent = nullptr);
void setFlag(bool isstop);//标记位
int m_nTimer;//计数
QMutex m_mutex;//互斥事件
void lockFun();//加锁函数,
void unlockFun();//解锁
bool bpause;//暂停---线程是否处于上锁状态
signals:
void readyResults(int nRes);//m_nTimer 每增加一个数,就给主程序发一次信号
public slots:
void doWork();//对应于主程序中的槽函数 开始
void stop();//结束
private:
volatile bool isStop;//是否要结束
QTimer *timer;
int nInterval;//定时器间隔时常
};
#endif // MTTHREAD_H
对应的源文件
#include "mtthread.h"
#include <QDebug>
static int ncount = 0;
#include <QDateTime>
#include <QElapsedTimer>
#pragma execution_character_set("utf-8")
mtThread::mtThread(QObject *parent) : QObject(parent)
{
isStop = true;
m_nTimer = 0;
nInterval = 500;
timer = new QTimer(this);
timer->start(nInterval);
bpause = false;
}
//【有问题】暂停后重新开始,定时器变快了,而且相隔1毫秒响应两次或者多次timeout
//解决:上锁后先关闭定时器,解锁后重新打开定时器
void mtThread::doWork()
{
connect(timer,&QTimer::timeout,this,[=](){
m_mutex.lock();//暂停时,就运行到此处的上方。
if(!isStop)
{
ncount++;
m_nTimer = ncount;
emit readyResults(m_nTimer);
qDebug()<<"object:---dowork3: "<<m_nTimer<<" "<<QDateTime::currentDateTime().toString("hh:mm:ss.zzz");
}
m_mutex.unlock();
});
//尝试添加第五个参数 , Qt::UniqueConnection 还是不行
}
void Sleep(int msec)
{
QElapsedTimer t;
t.start();
while(t.elapsed()<msec);
}
//使用sleep的方式//第二种方法
//void mtThread::doWork()
//{
// while (1) {
// if(isStop)
// {
// break;
// }
// m_mutex.lock();
// if(!isStop)
// {
// ncount++;
// m_nTimer = ncount;
// Sleep(500);
// emit readyResults(m_nTimer);
// qDebug()<<"object:---dowork: "<<m_nTimer<<" "<<QDateTime::currentDateTime().toString("hh:mm:ss.zzz");
// }
// m_mutex.unlock();
// }
//}
void mtThread::setFlag(bool isst)
{
isStop = isst;
qDebug()<<"object:---setflag: "<<m_nTimer<<isst;
}
//
void mtThread::lockFun()//上锁---关闭定时器
{
if(bpause)//加过锁之后,不要再加了,一层保护
{
return;
}
timer->stop();//先关闭定时器,然后再重新开启,这样定时器就对啦
m_mutex.lock();
bpause = true;
qDebug()<<"object:---lockFun: "<<" bpause = " <<bpause;
}
void mtThread::unlockFun()//已经解锁了或者处于未曾枷锁的过程中//解锁--重新开启定时器
{
if(bpause == false)
{
return;
}
m_mutex.unlock();//还是放前面吧
timer = new QTimer(this);
timer->start(nInterval);
bpause = false;
//m_mutex.unlock();//好像放哪里都可以呢
qDebug()<<"object:---unlockFun: "<<" bpause = " <<bpause;
}
void mtThread::stop()
{
qDebug()<<"object:---stop: "<<m_nTimer<<" bpause = " <<bpause;
//m_mutex.unlock();//有时候状态不对,强制解锁会造成程序崩溃的.如果已经解锁了,在此解锁会崩溃的。
isStop = true;
// if(bpause == true)//在暂停状态下//必须在发送sendStop信号之前进行解锁。如果用此处的话,则可能运行不到就卡死了,因为新线程杀掉了,而此处的新类还没释放掉
// {
// m_mutex.unlock();
// qDebug()<<"object:- ??????????";
// }
if(timer->isActive())
timer->stop();//结束定时器
}
主界面中头文件
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QThread>
#include "mtthread.h"
//#include <QMutexLocker>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
~Widget();
void dealResult();
// void showEvent(QShowEvent *e);
signals:
void sendStop();
private slots:
void on_opem_btn_clicked();
void on_close_btn_clicked();
void on_pushButton_2_clicked();
void on_pushButton_clicked();
private:
Ui::Widget *ui;
QThread mythread;//新线程对象
mtThread *mt;//新类指针
bool isFirstClicked;
};
#endif // WIDGET_H
主界面中源文件
#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
#include <QTimer>
#include <QDebug>
#include <QDateTime>
#pragma execution_character_set("utf-8")
//extern QMutex m_mutex;
//哟有一个问题:即使不暂停的话,界面显示 不正常,有时候会少显示一点点,但总数是对的
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
mt = new mtThread();
mt->moveToThread(&mythread);
connect(&mythread,&QThread::finished,mt,&QObject::deleteLater);//新线程与新类//这个容易出错 槽函数写不对
connect(ui->opem_btn,&QPushButton::clicked,mt,&mtThread::doWork);//widget与新类 点击按钮 开始 进行处理
//connect(ui->close_btn,&QPushButton::clicked,mt,&mtThread::stop);//widget与新类 点击按钮 结束
connect(this,&Widget::sendStop,mt,&mtThread::stop);
// connect(mt,&mtThread::readyResults,this,[=](){
// ui->lcdNumber->display(QString("%1").arg(mt->m_nTimer));
// //this->update();//增加一个刷新//4还是显示不出来
// //qApp->processEvents();//也不管用
// });//新类与widget 交互信息,进行显示 新类中数据处理完后,显示在界面上//有问题,显示不实时,有漏数
//下面能够实时显示计数,而且不漏。
//直接读的emit信号传过来的数据,这样就可以实时显示拉。而上面那个方法是读取的新类中的全局变量,在lcd显示时,会漏掉一些。
void (mtThread:: *psignal)(int res) = &mtThread::readyResults;
connect(mt,psignal,this,[=](int res){
ui->lcdNumber->display(QString("%1").arg(res));
qDebug()<<"getresult:--- "<<QDateTime::currentDateTime().toString("hh:mm:ss.zzz");
});//新类与widget 交互信息,进行显示 新类中数据处理完后,显示在界面上
connect(ui->pushButton_2,&QPushButton::clicked,mt,&mtThread::doWork);//重新开始
isFirstClicked = false;
// //写一个定时器测试//界面不会漏 显示
// QTimer *timer1 = new QTimer();
// timer1->start(500);
// static int ir = 0;
// connect(timer1,&QTimer::timeout,this,[=](){
// ui->lcdNumber->display(QString("%1").arg(ir));
// qDebug()<<" ir = "<<ir;
// ir++;
// });
}
//https://blog.csdn.net/jiedesheng/article/details/79481496?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task
//重写//QT的刷新机制,有时会不刷新的bug解决办法//也不管用
//void Widget::showEvent(QShowEvent *e)
//{
// this->setAttribute(Qt::WA_Mapped);
// QWidget::showEvent(e);
//}
Widget::~Widget()
{
delete ui;
}
void Widget::on_opem_btn_clicked()
{
if(mythread.isRunning())
{
qDebug()<<"widget中线程正在运行";
return;
}
if(isFirstClicked == true)//非第一次点击
{
mt->m_nTimer = 0;//重新计数
mt = new mtThread();
mt->moveToThread(&mythread);
qDebug()<<"关闭后重新打开";
}
if(isFirstClicked == false)
{
isFirstClicked = true;
}
mt->setFlag(false);
mythread.start();
}
void Widget::on_close_btn_clicked()
{
qDebug()<<"关闭按钮";
if(mythread.isRunning())
{
qDebug()<<"isrunning?????";
if(mt->bpause)//如果处于暂停状态,关闭时,要先解锁。如果不解锁,则程序会崩溃的
{
mt->unlockFun();//第二种方法 ;
}
emit sendStop();//由于是多线程,如果是暂停状态,则一定要先解锁,再去操作其他的,所以,上面那句,如果是中断的话,一定要先解锁才可以。
mt->setFlag(true);
qDebug()<<"新线程结束1";
mythread.quit();//析构时,将新线程释放内存 ; 如果新线程在初始化时 new this了就不用此处析构拉
qDebug()<<"新线程结束2";//入股没有上面的解锁,则会运行到此就卡死了。新线程结束了,而新类还在响应。。。
mythread.wait();
qDebug()<<"新线程结束";
}
}
void Widget::on_pushButton_2_clicked()//继续写
{
//mt->m_mutex.unlock();
//QMutexLocker m_lock(&m_mtex);//可以用这个方法
mt->unlockFun();//第二种方法
qDebug()<<"解锁--重新开始计数";
}
void Widget::on_pushButton_clicked()
{
// mt->m_mutex.lock();
mt->lockFun();
qDebug()<<"暂停,上锁";
}
其中出现的问题
1.重新计数后,定时器频率变了,变为原来好几倍。
解决办法:上锁时定时器关掉,解锁时重新启动
2.主界面不能实时显示计数
解决办法a: 界面显示时,读取子类传递过来的信号参数,而不是直接读取子类中的全局值。
自己写的程序,如果有什么错误,希望大家能够指出来,谢谢!!!
具体代码链接
https://download.csdn.net/download/u012719076/12228826