目录
1.概要
需求:有些时候,我们要运行一个函数,但是这个函数的运行,需要在一个条件满足后。
这种情况,用信号和槽,是一个好的处理办法。
但是由于一些其他原因无法使用信号和槽的方式。所以就用一个定周期函数监控运行这个函数需要的条件,不满足就继续监听,一旦条件满足,就结束监听,执行函数。
2.内容
1.
在 Qt C++ 中实现一个周期性执行的函数,且依赖外部状态触发停止,可以通过 QTimer
结合状态检查来实现。以下是分步解决方案:
1. 核心思路
- 周期性触发:使用
QTimer
的重复定时器(setInterval
+start()
)。 - 状态依赖:在定时器的回调函数中检查外部状态。
- 终止条件:当状态满足时,执行目标函数并停止定时器。
2. 实现代码示例
头文件(MyClass.h)
#include <QObject>
#include <QTimer>
class MyClass : public QObject {
Q_OBJECT
public:
explicit MyClass(QObject *parent = nullptr);
// 设置外部状态(线程安全示例)
void setExternalState(bool state);
private slots:
void onTimerTimeout();
private:
QTimer *m_timer;
bool m_externalState; // 外部状态变量
QMutex m_mutex; // 保护多线程访问
};
源文件(MyClass.cpp)
#include "MyClass.h"
#include <QDebug>
MyClass::MyClass(QObject *parent) : QObject(parent), m_externalState(false) {
m_timer = new QTimer(this);
connect(m_timer, &QTimer::timeout, this, &MyClass::onTimerTimeout);
// 启动定时器,假设周期为 1000 毫秒
m_timer->start(1000);
}
void MyClass::setExternalState(bool state) {
QMutexLocker locker(&m_mutex);
m_externalState = state;
}
void MyClass::onTimerTimeout() {
QMutexLocker locker(&m_mutex);
if (m_externalState) {
qDebug() << "外部状态满足,执行操作并停止定时器...";
// 执行你的目标函数
doSomething();
m_timer->stop();
} else {
qDebug() << "外部状态未满足,继续等待...";
}
}
void MyClass::doSomething() {
// 你的业务逻辑
}
3. 关键点说明
- 线程安全:
- 如果外部状态可能由多线程修改,使用
QMutex
保护m_externalState
。 - 通过
QMutexLocker
自动管理锁的获取和释放。
- 如果外部状态可能由多线程修改,使用
- 定时器控制:
m_timer->start(1000)
:启动定时器,每 1000 毫秒触发一次。m_timer->stop()
:当状态满足时停止定时器。
- 状态检查:
- 在
onTimerTimeout
中检查m_externalState
,满足条件则执行操作并停止定时器。
- 在
4. 扩展场景
- 单次触发:若只需在状态满足时执行一次,可将定时器设为单次模式(
setSingleShot(true)
)。 - 动态周期:通过
setInterval()
动态调整定时器周期。 - 异步通知:若外部状态由其他模块控制,可通过信号槽通知状态变化,避免轮询。
5. 使用示例
int main() {
MyClass obj;
// 模拟外部状态变化(例如:5 秒后状态变为 true)
QTimer::singleShot(5000, [&]() {
obj.setExternalState(true);
});
return QCoreApplication::exec();
}
此方案通过 QTimer
实现周期性检查,结合状态变量控制执行逻辑,简洁高效地满足了需求。
2. 完整实例
1.代码
#include "PeriodicExecutor.h"
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
PeriodicExecutor executor;
// 模拟5秒后外部条件变为true
QTimer::singleShot(5000, [&]() {
qDebug() << "模拟外部条件变化";
executor.setExternalCondition(true);
});
return a.exec();
}
#ifndef PERIODICEXECUTOR_H
#define PERIODICEXECUTOR_H
// main.cpp
#include <QCoreApplication>
#include <QTimer>
#include <QDebug>
#include <QMutex>
class PeriodicExecutor : public QObject {
Q_OBJECT
public:
explicit PeriodicExecutor(QObject *parent = nullptr) : QObject(parent) {
m_timer = new QTimer(this);
connect(m_timer, &QTimer::timeout, this, &PeriodicExecutor::checkCondition);
m_timer->start(1000); // 每1秒检查一次
}
// 设置外部状态(线程安全)
void setExternalCondition(bool condition) {
QMutexLocker locker(&m_mutex);
m_externalCondition = condition;
}
private slots:
void checkCondition() {
QMutexLocker locker(&m_mutex);
if (m_externalCondition) {
qDebug() << "条件满足,执行操作并停止检查...";
executeTask();
m_timer->stop();
} else {
qDebug() << "条件未满足,继续等待...";
}
}
private:
void executeTask() {
qDebug() << "执行目标函数...";
// 在此处添加你的业务逻辑
}
QTimer *m_timer;
bool m_externalCondition = false;
QMutex m_mutex;
};
#endif // PERIODICEXECUTOR_H
2. 运行结果
条件未满足,继续等待...
条件未满足,继续等待...
条件未满足,继续等待...
条件未满足,继续等待...
条件未满足,继续等待...
模拟外部条件变化
条件满足,执行操作并停止检查...
执行目标函数...
3.关联链接
4.关联知识
1.QMutexLocker
QMutexLocker
是 Qt 框架中用于简化互斥锁(Mutex)管理的辅助类。它通过 RAII(Resource Acquisition Is Initialization) 技术,确保锁在作用域内自动获取和释放,避免手动管理锁可能导致的死锁或资源泄漏。
核心作用
- 自动加锁/解锁:
- 构造函数中自动调用
QMutex::lock()
。 - 析构函数中自动调用
QMutex::unlock()
,即使发生异常也能保证解锁。
- 构造函数中自动调用
- 防止死锁:
- 避免因忘记解锁或异常导致的锁未释放。
- 代码简洁性:
- 无需手动编写
lock()
和unlock()
对。
- 无需手动编写
基础用法
#include <QMutex>
#include <QMutexLocker>
QMutex mutex;
int sharedData = 0;
void threadSafeFunction() {
QMutexLocker locker(&mutex); // 自动加锁
sharedData++; // 安全访问共享资源
// locker 离开作用域时自动解锁
}
与手动锁对比
手动锁(易出错)
mutex.lock();
try {
sharedData++;
} catch (...) {
mutex.unlock(); // 异常时可能忘记解锁
throw;
}
mutex.unlock();
QMutexLocker(安全)
QMutexLocker locker(&mutex); // 自动加锁
sharedData++; // 异常时自动解锁
// locker 析构时自动解锁
关键特性
- 作用域控制:
- 锁的生命周期与
QMutexLocker
对象的作用域绑定。 - 适合局部代码块的同步。
- 锁的生命周期与
- 线程安全:
- 即使多个线程同时竞争锁,也能保证只有一个线程访问临界区。
- 性能:
- 无额外性能开销,与手动锁效率相同。
适用场景
- 保护共享资源(如全局变量、数据结构)。
- 多线程环境下对临界区的安全访问。
- 需要确保异常安全(Exception Safety)的代码段。
注意事项
- 作用域设计:
- 避免锁的作用域过大,导致其他线程长时间阻塞。
- 锁应仅保护必要的临界区代码。
- 递归锁:
- 如果使用
QMutex::Recursive
模式,需确保递归调用不会导致死锁。
- 如果使用
- 读写锁优化:
- 对读多写少的场景,可结合
QReadWriteLock
和QReadLocker
/QWriteLocker
提高并发性能。
- 对读多写少的场景,可结合
通过 QMutexLocker
,你可以更安全、简洁地管理多线程同步问题,是 Qt 多线程编程中的推荐实践。
2.QObject
QObject
是 Qt 框架的核心基类,所有 Qt 对象都直接或间接继承自它。它提供了对象模型、信号槽机制、属性系统、事件处理等关键功能,是 Qt 实现跨平台、高扩展性应用的基础。
核心功能解析
1. 对象模型与元对象系统
- 对象树:通过父子关系管理对象生命周期,父对象销毁时自动删除所有子对象。
QObject *parent = new QObject; QObject *child = new QObject(parent); // child 会随 parent 自动销毁
- 元对象编译器 (moc):通过
Q_OBJECT
宏启用元对象系统,生成额外的代码支持信号槽、属性等。
2. 信号与槽 (Signal & Slot)
- 类型安全的观察者模式:用于对象间通信,替代传统回调函数。
// 声明信号(在类头文件中) signals: void valueChanged(int newValue); // 声明槽函数 public slots: void handleValueChanged(int value); // 连接信号与槽 connect(sender, &Sender::valueChanged, receiver, &Receiver::handleValueChanged);
3. 属性系统 (Properties)
- 通过
Q_PROPERTY
宏声明属性,支持动态读写和绑定。Q_PROPERTY(int speed READ speed WRITE setSpeed NOTIFY speedChanged)
- 属性可在 Qt Designer 中直接编辑,或通过
setProperty()
/property()
动态修改。
4. 事件处理 (Events)
- 重写
event()
函数处理系统事件(如鼠标、键盘事件)。bool MyWidget::event(QEvent *event) { if (event->type() == QEvent::MouseButtonPress) { qDebug() << "Mouse pressed!"; return true; // 事件已处理 } return QWidget::event(event); // 交给基类处理 }
5. 国际化支持 (I18N)
- 使用
tr()
函数标记可翻译字符串,结合.ts
文件实现多语言支持。QLabel *label = new QLabel(tr("Hello World"));
6. 定时器 (Timers)
- 通过
QTimer
实现单次或周期性任务。QTimer *timer = new QTimer(this); connect(timer, &QTimer::timeout, this, [](){ qDebug() << "Timer triggered!"; }); timer->start(1000); // 每秒触发一次
7. 动态对象信息
- 运行时类型识别(RTTI):通过
qobject_cast
进行安全类型转换。if (QObject *obj = qobject_cast<MyWidget*>(someObject)) { // 安全使用 obj }
关键设计优势
- 跨平台抽象:隐藏操作系统差异,提供统一 API。
- 松耦合架构:信号槽机制解耦对象间的直接依赖。
- 工具链集成:与 Qt Designer、Qt Linguist 等工具无缝协作。
- 内存安全:对象树自动管理内存,减少泄漏风险。
典型应用场景
- GUI 开发:QWidget 及其子类构建界面。
- 多线程通信:信号槽默认线程安全(需配合
Qt::AutoConnection
)。 - 插件系统:通过元对象系统动态加载和调用插件。
- 脚本集成:暴露对象给 QML 或 Python/JavaScript 脚本。
示例:完整的信号槽交互
// Sender.h
class Sender : public QObject {
Q_OBJECT
public:
void triggerSignal() { emit mySignal(); }
signals:
void mySignal();
};
// Receiver.h
class Receiver : public QObject {
Q_OBJECT
public slots:
void handleSignal() { qDebug() << "Signal received!"; }
};
// main.cpp
int main() {
Sender sender;
Receiver receiver;
QObject::connect(&sender, &Sender::mySignal, &receiver, &Receiver::handleSignal);
sender.triggerSignal(); // 输出 "Signal received!"
return 0;
}
通过 QObject
,Qt 提供了一套完整的对象生命周期管理、通信和扩展机制,显著简化了 C++ 跨平台应用的开发复杂度。
2.QTimer
QTimer
是 Qt 框架中用于定时任务的类,支持单次或周期性触发事件。它基于事件循环机制,与信号槽无缝集成,是 Qt 实现非阻塞定时操作的推荐方式。
核心功能
-
单次触发:指定延迟后触发一次
timeout()
信号。QTimer::singleShot(1000, [](){ qDebug() << "1秒后执行"; });
-
周期性触发:按固定间隔重复触发
timeout()
信号。QTimer timer; timer.setInterval(500); // 500ms间隔 connect(&timer, &QTimer::timeout, [](){ qDebug() << "每0.5秒执行"; }); timer.start();
-
高精度计时:通过
setTimerType()
设置计时器类型(如Qt::PreciseTimer
)。QTimer timer; timer.setTimerType(Qt::PreciseTimer); // 提高精度(平台依赖)
-
对象生命周期管理:
- 若父对象被销毁,关联的
QTimer
会自动停止并删除。
QObject parent; QTimer timer(&parent); // parent销毁时,timer自动停止
- 若父对象被销毁,关联的
关键特性
- 线程安全:
- 默认与创建它的线程关联,跨线程使用需显式指定线程:
QTimer timer; timer.moveToThread(otherThread); // 移动到其他线程
- 默认与创建它的线程关联,跨线程使用需显式指定线程:
- 与事件循环集成:
- 依赖
QEventLoop
,适合 GUI 或异步任务。 - 在非 GUI 线程中使用时,需确保有运行中的事件循环。
- 依赖
- 精度与效率:
- 单次定时器:精度较高(通常误差 <5ms)。
- 重复定时器:受系统调度和事件循环负载影响,间隔可能不绝对精确。
典型应用场景
-
延迟执行:
QTimer::singleShot(2000, this, [this](){ updateUI(); });
-
周期性更新:
QTimer timer; connect(&timer, &QTimer::timeout, this, &MyClass::updateData); timer.start(1000); // 每秒更新数据
-
超时控制:
QTimer timeoutTimer; timeoutTimer.setSingleShot(true); connect(&timeoutTimer, &QTimer::timeout, [](){ qDebug() << "操作超时!"; }); timeoutTimer.start(5000); // 5秒后触发超时
-
动画/游戏循环:
QTimer gameLoopTimer; gameLoopTimer.setInterval(16); // 约60FPS connect(&gameLoopTimer, &QTimer::timeout, this, &Game::updateFrame); gameLoopTimer.start();
最佳实践
- 避免阻塞操作:
timeout()
槽函数应快速执行,避免阻塞事件循环。
- 对象所有权:
- 若
QTimer
无父对象,需手动管理内存:QTimer *timer = new QTimer; // 使用后需 delete
- 若
- 跨线程使用:
- 在子线程中创建
QTimer
时,确保线程有运行中的事件循环:QThread thread; QTimer timer; timer.moveToThread(&thread); connect(&timer, &QTimer::timeout, [](){ qDebug() << "子线程定时任务"; }); thread.start(); timer.start(1000);
- 在子线程中创建
与 std::chrono
对比
特性 | QTimer | std::chrono |
---|---|---|
集成性 | 与 Qt 事件循环无缝集成 | 需手动实现循环或异步等待 |
信号槽支持 | 原生支持 | 需结合其他机制(如 Lambda) |
跨平台一致性 | 统一 API,行为一致 | 依赖系统定时器精度 |
适用场景 | GUI/异步任务 | 高精度测量、后台任务 |
通过 QTimer
,开发者可以高效实现定时任务,同时保持代码简洁性和可维护性,是 Qt 异步编程的重要工具。