qt中Qthread线程的使用以及安全关闭

前言

以前第一次使用线程的时候总是无法实现自己想要的效果,且为解决线程安全关闭的问题,今天看到一位老师讲了关于线程的使用方法,理解虽然还不是特别深刻,但已初步总结出相关注意事项。


环境:

  • qt5.12.12
  • windows操作系统

使用线程思路:

  1. 新建线程类,继承QThread
  2. 重写run()方法,将复杂程序都写在run的实现中,并设置循环条件(记得有些资料说线程必须写循环)
  3. 通过start()方法开启线程,通过requestInterruption()方法发出中断请求来跳出循环条件,跳出循环后该线程结束
  4. 在线程类中设置信号槽,当线程结束时杀死线程
  5. 在线程的析构函数中也要添加requestInterruption()中断请求,防止强制关闭窗口时线程不能安全关闭

先列举重要代码:(记得类要继承QThread)

AudioThread.h文件

void run() override;

AudioThread.cpp文件

//构造函数
AudioThread::AudioThread(QObject *parent) : QThread(parent)
{
    
    
    // 当监听到线程结束时(finished),就调用deleteLater回收内存
    connect(this,&AudioThread::finished,this,[=](){
    
    
        this->deleteLater();
        qDebug()<<"线程结束";
    });
}

//析构函数
AudioThread::~AudioThread()
{
    
    
    //强制关闭窗口时,线程也能安全关闭
    requestInterruption();
    wait();
    qDebug()<<"析构函数";
}

void AudioThread::run()
{
    
    
	.....自定义代码
    while(!isInterruptionRequested()){
    
    //当没发出中断请求时,执行循环体
        .....自定义代码
    }
    .....自定义代码
}

开启线程:

	AudioThread *audio_thread=new AudioThread(this);
    audio_thread->start();

关闭线程:

    audio_thread->requestInterruption();
    audio_thread->wait();	//等待线程结束才能执行下面代码
    audio_thread=nullptr;

完整代码:(我这里是通过线程调用ffmpeg实现音频录制,看起来会有点复杂,可只关注重要代码,也不要盲目复制粘贴,运行之前需要先配置ffmpeg)

AudioThread.h文件

#ifndef AUDIOTHREAD_H
#define AUDIOTHREAD_H

#include <QObject>
#include <QThread>

class AudioThread : public QThread
{
    
    
    Q_OBJECT
public:
    explicit AudioThread(QObject *parent = nullptr);
    ~AudioThread();

private:
    void run() override;
signals:

};

#endif // AUDIOTHREAD_H

AudioThread.cpp文件

#include "audiothread.h"

#include <QDebug>
#include <QFile>

extern "C"{
    
    
// 设备相关API
#include <libavdevice/avdevice.h>
// 格式相关API
#include <libavformat/avformat.h>
// 工具相关API(比如错误处理)
#include <libavutil/avutil.h>
// 编码相关API
#include <libavcodec/avcodec.h>
}


#ifdef Q_OS_WIN
    // PCM文件的文件名
    #define FILENAME "E:/media/out.pcm"
#else
    #define FILENAME "/Users/mj/Desktop/out.pcm"
#endif

AudioThread::AudioThread(QObject *parent) : QThread(parent)
{
    
    
    // 当监听到线程结束时(finished),就调用deleteLater回收内存
    connect(this,&AudioThread::finished,this,[=](){
    
    
        this->deleteLater();
        qDebug()<<"线程结束";
    });
}

AudioThread::~AudioThread()
{
    
    
    //强制关闭窗口时,线程也能安全关闭
    requestInterruption();
    wait();
    qDebug()<<"析构函数";
}

void AudioThread::run()
{
    
    

    char *FMT_NAME="dshow";
    const AVInputFormat *localAv_find_input_format = av_find_input_format(FMT_NAME);
    if(!localAv_find_input_format){
    
    
        qDebug()<<"找不到输入格式"<<FMT_NAME;
        return;
    }
    qDebug()<<FMT_NAME;
    AVFormatContext *ctx=nullptr;
    char *device_name="audio=麦克风 (Realtek High Definition Audio)";
    int ret = avformat_open_input(&ctx,device_name,localAv_find_input_format,nullptr);
    if(ret!=0){
    
    
        char errbuf[1024];
        av_strerror(ret,errbuf,sizeof(errbuf));
        qDebug()<<"打开设备失败"<<ret<<errbuf;
        return;
    }

    QFile file(FILENAME);
    if(!file.open(QIODevice::WriteOnly)){
    
    
        qDebug()<<"文件打开失败"<<FILENAME;
        avformat_close_input(&ctx);
        return;
    }

    AVPacket *pkt = av_packet_alloc();
    while(!isInterruptionRequested()){
    
    
        //采集数据
        ret = av_read_frame(ctx,pkt);

        if(ret==0){
    
    
            //向文件中写
            file.write((char *)pkt->data,pkt->size);
            qDebug()<<"正在录制,文件正在写入";

            //释放资源
            av_packet_unref(pkt);
        }else if (ret == AVERROR(EAGAIN)) {
    
     // 资源临时不可用
            continue;
        }
        else{
    
    
            char errbuf[1024];
            av_strerror(ret,errbuf,sizeof(errbuf));
            qDebug()<<"av_read_frame出错"<<errbuf<<sizeof (errbuf);
            return;
        }
        av_packet_unref(pkt);
    }

    file.close();
    //释放资源
    av_packet_free(&pkt);
    //关闭设备
    avformat_close_input(&ctx);
    qDebug()<<"录制成功结束";

    return;
}

主线程调用子线程:(需提前定义全局变量AudioThread *audio_thread=nullptr;bool is_record,is_abnormal;)

void MainWindow::on_pushButton_clicked()
{
    
    
	if(!is_record){
    
    
        audio_thread=new AudioThread(this);
        audio_thread->start();
        //防止强制关闭窗口,线程无法正常退出
        connect(audio_thread,&AudioThread::finished,audio_thread,[=](){
    
    
            ui->pushButton->setText("开始录制");
            is_record=false;
            if(is_abnormal){
    
    
                qDebug()<<"线程异常结束";
            }
        });
        qDebug()<<"开始录制";
        ui->pushButton->setText("结束录制");
        is_record=true;
    }else{
    
    
        is_abnormal=false;
        audio_thread->requestInterruption();
        audio_thread->wait();
        audio_thread=nullptr;
        qDebug()<<"结束录制";
        ui->pushButton->setText("开始录制");
        is_record=false;
    }
}

程序效果:

image-20220614152640503

感觉QThread使用起来注意事项还是比较多的,后续有新的理解会继续补充


码字不易,如果这篇博客对你有帮助,麻烦点赞收藏,非常感谢!有不对的地方

猜你喜欢

转载自blog.csdn.net/weixin_44092851/article/details/125279155
今日推荐