文章目录
需求
调用第三方库的一个耗时函数,需要等待返回结果进行下一步,但是又不想阻塞Qt界面。
解决思路
创建子线程等待主线程调用,在主线程使用QEventLoop循环等待子线程返回执行结果,子线程内使用QWaitCondition,主要用于多线程编程,它提供了一种机制来同步线程间的操作,通过调用wakeOne()或wakeAll()方法唤醒等待的线程。
子线程
子线程继承QThread类,在run函数内执行任务,执行完后发送信号给主线程。
示例如下:
子线程头文件:
cpp
// 子线程 防止阻塞主进程
#pragma once
#include <QThread>
#include <QMutex>
#include <QWaitCondition>
#include <QDebug>
class QThreadControl : public QThread
{
Q_OBJECT
public:
QThreadControl(QObject* parent = nullptr);
~QThreadControl();
// 子线程执行函数
void run() override;
// 结束子线程
void stop();
// 等待返回数据
QByteArray result();
signals:
// 子线程执行完任务通知主线程
void taskFinished(QByteArray);
public:
// 发送数据并等待返回值的函数
void sendSerialCommand(const QByteArray& command, CAMERA _current_camera);
// 设置相关参数
void setParameter(const QByteArray& command, CAMERA _current_camera);
bool m_active; // 保持线程活跃标志位
自定义类对象 current_item_; // 当前对象
QByteArray command_; // 控制指令
QByteArray result_; // 返回信息
mutable QMutex m_mutex; // 多线程加锁
// 唤醒等待的线程 与互斥锁(QMutex)一起使用 以保护共享数据并同步线程
QWaitCondition m_condition;
};
子线程源文件:
cpp
#include "QThreadControl.h"
#include<Windows.h>
QThreadControl::QThreadControl(QObject* parent)
{
m_active = true;
}
QThreadControl::~CameraCtrolLens()
{
stop();
wait();
}
void QThreadControl::run()
{
while (m_active) {
QMutexLocker locker(&m_mutex);
m_condition.wait(&m_mutex);
if (!m_active) break;
// 执行任务
sendSerialCommand(command_, current_item_);
// 任务完成后发送信号回主线程
emit taskFinished(result_);
}
}
void QThreadControl::stop()
{
m_active = false;
m_condition.wakeOne();
}
QByteArray QThreadControl::result()
{
QMutexLocker locker(&m_mutex);
m_condition.wait(&m_mutex, 5000); // 等待最多5秒
return result_;
}
void QThreadControl::sendSerialCommand(const QByteArray& command,CAMERA _current_camera)
{
QByteArray revice_data(16, char(0));
if (_current_camera != nullptr) {
int rev_num = 0;
bool is_reviced = false;
while (!is_reviced)
{
if (revice_data.data() == QByteArray("waiting\r\n") || revice_data.data() == QByteArray(""))
{
Sleep(100);
//qDebug() << "send lens command waiting...";
++rev_num;
}
else
{
is_reviced = true;
break;
}
if (rev_num >= 80)
{
revice_data.append("ERROR");
break;
}
}
}
else
{
revice_data.append("ERROR");
}
result_ = revice_data;
}
void QThreadControl::setParameter(const QByteArray& command, 自定义类对象 _current_item)
{
QMutexLocker locker(&m_mutex);
current_camera_ = _current_camera;
command_ = command;
m_condition.wakeOne();
}
主线程
主线程调用如下所示,先设置参数,后创建loop循环,该循环会一直等待子线程任务结束退出,保证了返回值的数据同步,同时不会阻塞主界面操作。需要注意的是,主线程冲突按钮需要设置为不可用状态。
cpp
QByteArray test;
test.append(_send_order);
camera_ctrol_lens_->setParameter(test,current_camera_);
// 创建事件循环对象 为了阻塞该函数等待结果返回
QEventLoop loop;
// 主循环,这里我们不需要循环,因为只需要执行一次任务
QObject::connect(camera_ctrol_lens_, &CameraCtrolLens::taskFinished, &loop, &QEventLoop::quit);
// 进入事件循环,等待线程完成
loop.exec();
// 获取结果
return rev_value_;