小项目实践:定周期监控一个程序运行的条件

目录

​编辑

1.概要

2.内容

1.

1. 核心思路

2. 实现代码示例

头文件(MyClass.h)

源文件(MyClass.cpp)

3. 关键点说明

4. 扩展场景

5. 使用示例

2. 完整实例

1.代码

2. 运行结果

3.关联链接

4.关联知识

1.QMutexLocker

核心作用

基础用法

与手动锁对比

手动锁(易出错)

QMutexLocker(安全)

关键特性

适用场景

注意事项

2.QObject 

核心功能解析

1. 对象模型与元对象系统

2. 信号与槽 (Signal & Slot)

3. 属性系统 (Properties)

4. 事件处理 (Events)

5. 国际化支持 (I18N)

6. 定时器 (Timers)

7. 动态对象信息

关键设计优势

典型应用场景

示例:完整的信号槽交互

2.QTimer 

核心功能

关键特性

典型应用场景

最佳实践

与 std::chrono 对比


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. 关键点说明

  1. 线程安全
    • 如果外部状态可能由多线程修改,使用 QMutex 保护 m_externalState
    • 通过 QMutexLocker 自动管理锁的获取和释放。
  2. 定时器控制
    • m_timer->start(1000):启动定时器,每 1000 毫秒触发一次。
    • m_timer->stop():当状态满足时停止定时器。
  3. 状态检查
    • 在 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) 技术,确保锁在作用域内自动获取和释放,避免手动管理锁可能导致的死锁或资源泄漏。


核心作用

  1. 自动加锁/解锁
    • 构造函数中自动调用 QMutex::lock()
    • 析构函数中自动调用 QMutex::unlock(),即使发生异常也能保证解锁。
  2. 防止死锁
    • 避免因忘记解锁或异常导致的锁未释放。
  3. 代码简洁性
    • 无需手动编写 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 析构时自动解锁

关键特性

  1. 作用域控制
    • 锁的生命周期与 QMutexLocker 对象的作用域绑定。
    • 适合局部代码块的同步。
  2. 线程安全
    • 即使多个线程同时竞争锁,也能保证只有一个线程访问临界区。
  3. 性能
    • 无额外性能开销,与手动锁效率相同。

适用场景

  • 保护共享资源(如全局变量、数据结构)。
  • 多线程环境下对临界区的安全访问。
  • 需要确保异常安全(Exception Safety)的代码段。

注意事项

  1. 作用域设计
    • 避免锁的作用域过大,导致其他线程长时间阻塞。
    • 锁应仅保护必要的临界区代码。
  2. 递归锁
    • 如果使用 QMutex::Recursive 模式,需确保递归调用不会导致死锁。
  3. 读写锁优化
    • 对读多写少的场景,可结合 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
    }

关键设计优势

  1. 跨平台抽象:隐藏操作系统差异,提供统一 API。
  2. 松耦合架构:信号槽机制解耦对象间的直接依赖。
  3. 工具链集成:与 Qt Designer、Qt Linguist 等工具无缝协作。
  4. 内存安全:对象树自动管理内存,减少泄漏风险。

典型应用场景

  • 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 实现非阻塞定时操作的推荐方式。


核心功能

  1. 单次触发:指定延迟后触发一次 timeout() 信号。

    QTimer::singleShot(1000, [](){ qDebug() << "1秒后执行"; });
  2. 周期性触发:按固定间隔重复触发 timeout() 信号。

    QTimer timer;
    timer.setInterval(500); // 500ms间隔
    connect(&timer, &QTimer::timeout, [](){ qDebug() << "每0.5秒执行"; });
    timer.start();

  3. 高精度计时:通过 setTimerType() 设置计时器类型(如 Qt::PreciseTimer)。

    QTimer timer;
    timer.setTimerType(Qt::PreciseTimer); // 提高精度(平台依赖)
  4. 对象生命周期管理

    • 若父对象被销毁,关联的 QTimer 会自动停止并删除。

    QObject parent;
    QTimer timer(&parent); // parent销毁时,timer自动停止

关键特性

  1. 线程安全
    • 默认与创建它的线程关联,跨线程使用需显式指定线程:

      QTimer timer;
      timer.moveToThread(otherThread); // 移动到其他线程
  2. 与事件循环集成
    • 依赖 QEventLoop,适合 GUI 或异步任务。
    • 在非 GUI 线程中使用时,需确保有运行中的事件循环。
  3. 精度与效率
    • 单次定时器:精度较高(通常误差 <5ms)。
    • 重复定时器:受系统调度和事件循环负载影响,间隔可能不绝对精确。

典型应用场景

  1. 延迟执行

    QTimer::singleShot(2000, this, [this](){ updateUI(); });
  2. 周期性更新

    QTimer timer;
    connect(&timer, &QTimer::timeout, this, &MyClass::updateData);
    timer.start(1000); // 每秒更新数据
  3. 超时控制

    QTimer timeoutTimer;
    timeoutTimer.setSingleShot(true);
    connect(&timeoutTimer, &QTimer::timeout, [](){ qDebug() << "操作超时!"; });
    timeoutTimer.start(5000); // 5秒后触发超时
  4. 动画/游戏循环

    QTimer gameLoopTimer;
    gameLoopTimer.setInterval(16); // 约60FPS
    connect(&gameLoopTimer, &QTimer::timeout, this, &Game::updateFrame);
    gameLoopTimer.start();


最佳实践

  1. 避免阻塞操作
    • timeout() 槽函数应快速执行,避免阻塞事件循环。
  2. 对象所有权
    • 若 QTimer 无父对象,需手动管理内存:
       

      QTimer *timer = new QTimer;
      // 使用后需 delete

  3. 跨线程使用
    • 在子线程中创建 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 异步编程的重要工具。