当前位置: 首页 > news >正文

工业软件架构4:(QT和C++实现)

工业软件架构 - 事件驱动 - 4

  • 1. 任务依赖关系的管理
  • 2. 任务依赖管理器
  • 3. 任务类的定义
  • 4. 任务的执行
  • 5. 运行原理
  • 6. 忽略型任务
    • 6.1 任务状态检查机制
      • 任务状态管理器
      • 任务类定义
      • 主程序
      • 特点
      • 优点
      • 缺点
      • 适用场景
    • 6.2 任务队列过滤机制
      • 任务队列过滤器
      • 任务类定义
      • 主程序
      • 特点
      • 优点
      • 缺点
      • 适用场景
    • 6.3 基于信号与槽的任务忽略
      • 任务控制器
      • 任务类定义
      • 主程序
      • 特点
      • 优点
      • 缺点
      • 适用场景
    • 对比总结
    • 选择建议
  • 7. 总结

在一些复杂的系统中,任务之间可能存在相互影响的情况,比如任务 A 在运行时,任务 B 不能运行,或者任务 C 只能在任务 A 或 B 完成后才能运行。为了处理这些情况,需要设计一种机制来协调任务之间的关系,确保它们按照预期的顺序和条件执行。

1. 任务依赖关系的管理

要处理这种情况,我们需要引入任务依赖关系的概念。可以通过以下几种方式来管理任务之间的依赖关系:

  • 任务状态管理: 维护任务的状态(如正在运行、已完成、等待中),并根据状态决定是否可以执行某个任务。
  • 互斥锁(QMutex): 使用 QMutex 来防止某些任务在特定条件下同时运行。
  • 信号与槽机制: 使用信号与槽机制,在任务完成后通知其他任务是否可以启动。

2. 任务依赖管理器

创建一个任务依赖管理器(TaskDependencyManager)来协调任务之间的依赖关系。这个管理器将负责跟踪任务状态、管理互斥锁,以及协调任务的执行顺序。

#include <QObject>
#include <QMutex>
#include <QWaitCondition>
#include <QMap>
#include <QRunnable>
#include <QThreadPool>
#include <QDebug>class TaskDependencyManager : public QObject 
{Q_OBJECTpublic:enum TaskState {Waiting,Running,Completed};void registerTask(const QString &taskName){QMutexLocker locker(&mutex);taskStates[taskName] = Waiting;}void startTask(const QString &taskName, QRunnable *task){QMutexLocker locker(&mutex);if (canStartTask(taskName)) {taskStates[taskName] = Running;QThreadPool::globalInstance()->start(task);} else {qDebug() << "Task" << taskName << "is waiting for dependencies.";}}void completeTask(const QString &taskName){QMutexLocker locker(&mutex);taskStates[taskName] = Completed;condition.wakeAll();  // 唤醒所有等待的任务qDebug() << "Task" << taskName << "completed.";}bool canStartTask(const QString &taskName) {// 这里可以根据实际情况来定义依赖规则if (taskStates.contains("A") && taskStates["A"] == Running && taskName == "B") {return false;  // 如果任务A正在运行,则任务B不能启动}return true;}void waitForTaskCompletion(const QString &taskName){QMutexLocker locker(&mutex);while (taskStates[taskName] != Completed) {condition.wait(&mutex);}}private:QMutex mutex;QWaitCondition condition;QMap<QString, TaskState> taskStates;
};

3. 任务类的定义

定义任务类,任务在执行前需要先检查依赖管理器,以决定是否可以开始执行。

class TaskA : public QRunnable 
{
public:TaskA(TaskDependencyManager *manager): manager(manager) {}void run() override {manager->startTask("A", this);qDebug() << "Task A started";QThread::sleep(2);  // 模拟任务A的耗时操作qDebug() << "Task A finished";manager->completeTask("A");}private:TaskDependencyManager *manager;
};class TaskB : public QRunnable 
{
public:TaskB(TaskDependencyManager *manager): manager(manager) {}void run() override {manager->waitForTaskCompletion("A");  // 等待任务A完成manager->startTask("B", this);qDebug() << "Task B started";QThread::sleep(2);  // 模拟任务B的耗时操作qDebug() << "Task B finished";manager->completeTask("B");}private:TaskDependencyManager *manager;
};class TaskC : public QRunnable{
public:TaskC(TaskDependencyManager *manager): manager(manager) {}void run() override {manager->waitForTaskCompletion("A");  // 等待任务A完成manager->waitForTaskCompletion("B");  // 等待任务B完成manager->startTask("C", this);qDebug() << "Task C started";QThread::sleep(2);  // 模拟任务C的耗时操作qDebug() << "Task C finished";manager->completeTask("C");}private:TaskDependencyManager *manager;
};

4. 任务的执行

在主程序中,任务依赖管理器会负责启动任务,并根据任务之间的依赖关系决定任务的执行顺序。

int main(int argc, char *argv[]) 
{QCoreApplication app(argc, argv);TaskDependencyManager manager;manager.registerTask("A");manager.registerTask("B");manager.registerTask("C");TaskA *taskA = new TaskA(&manager);TaskB *taskB = new TaskB(&manager);TaskC *taskC = new TaskC(&manager);QThreadPool::globalInstance()->start(taskA);QThreadPool::globalInstance()->start(taskB);QThreadPool::globalInstance()->start(taskC);return app.exec();
}

5. 运行原理

  • Task A: 首先启动并运行,因为它不依赖于其他任务的状态。
  • Task B: 在任务 A 运行时,任务 B 将等待任务 A 完成后再启动。
  • Task C: 在任务 A 和任务 B 都完成后,任务 C 才会启动。

6. 忽略型任务

在某些情况下,我们需要设计一种机制来忽略特定的任务。例如,当任务 A 正在运行时,如果任务 B 被触发,任务 B 需要被忽略而不执行。这种需求可以通过几种方式实现,具体取决于任务的触发和调度机制。

6.1 任务状态检查机制

一种常见的方法是使用任务状态检查机制。在这种机制中,任务在启动时会检查某些条件(如其他任务的状态),如果条件不满足,就会选择不执行任务。任务状态可以通过全局或共享的变量来跟踪,并在任务之间共享。

任务状态管理器

首先,我们创建一个任务状态管理器,用于跟踪任务的状态(如正在运行或空闲)。

#include <QObject>
#include <QMutex>
#include <QDebug>class TaskStatusManager : public QObject 
{Q_OBJECTpublic:enum TaskState {Idle,Running};void setTaskState(const QString &taskName, TaskState state){QMutexLocker locker(&mutex);taskStates[taskName] = state;}TaskState getTaskState(const QString &taskName) {QMutexLocker locker(&mutex);return taskStates.value(taskName, Idle);}private:QMutex mutex;QMap<QString, TaskState> taskStates;
};

任务类定义

然后,我们定义两个任务 A 和 B。任务 A 在运行时,任务 B 如果被触发,将会被忽略。

class TaskA : public QRunnable{
public:TaskA(TaskStatusManager *statusManager): statusManager(statusManager) {}void run() override {statusManager->setTaskState("A", TaskStatusManager::Running);qDebug() << "Task A started";QThread::sleep(5);  // 模拟任务A的耗时操作qDebug() << "Task A finished";statusManager->setTaskState("A", TaskStatusManager::Idle);}private:TaskStatusManager *statusManager;
};class TaskB : public QRunnable 
{
public:TaskB(TaskStatusManager *statusManager): statusManager(statusManager) {}void run() override{// 检查任务A的状态if (statusManager->getTaskState("A") == TaskStatusManager::Running){qDebug() << "Task B ignored because Task A is running";return;  // 忽略任务B}qDebug() << "Task B started";QThread::sleep(2);  // 模拟任务B的耗时操作qDebug() << "Task B finished";}private:TaskStatusManager *statusManager;
};

主程序

最后,在主程序中,我们初始化任务状态管理器并启动任务 A 和任务 B。任务 B 将会在任务 A 运行期间被忽略。

#include <QCoreApplication>
#include <QThreadPool>int main(int argc, char *argv[]) 
{QCoreApplication app(argc, argv);TaskStatusManager statusManager;TaskA *taskA = new TaskA(&statusManager);TaskB *taskB = new TaskB(&statusManager);QThreadPool *pool = QThreadPool::globalInstance();// 启动任务Apool->start(taskA);// 模拟任务B在任务A运行期间被触发QThread::sleep(1);  // 等待任务A开始运行pool->start(taskB);return app.exec();
}

特点

  • 任务内部检查: 在任务运行时检查其他任务的状态决定是否继续执行。任务自己负责判断是否需要执行或忽略。
  • 动态灵活: 任务在执行时动态判断状态,适合任务在执行过程中需要实时判断条件的场景。
  • 独立性强: 每个任务独立进行状态检查,适合任务数量较少或任务逻辑相对简单的场景。

优点

  • 易于实现: 只需在任务的开头添加状态检查逻辑即可,无需额外的队列或复杂的事件系统。
  • 独立性: 每个任务都可以独立决定是否继续执行,降低了任务之间的耦合性。
  • 实时判断: 适合需要在任务运行时动态判断是否需要继续执行的场景。

缺点

  • 管理复杂性: 当任务数量增多或依赖关系复杂时,每个任务都需要包含检查逻辑,管理和维护的复杂性增加。
  • 可能的资源浪费: 如果任务被启动后才发现不应执行(如在开头状态检查后发现),可能会造成资源浪费(如线程启动的开销)。

适用场景

  • 适用于任务数量较少、任务逻辑相对简单的场景。
  • 适用于任务在运行过程中需要实时判断是否继续执行的情况。

6.2 任务队列过滤机制

另一种方法是使用任务队列过滤机制。在任务进入队列之前或从队列中取出时,检查其是否应该被忽略。如果某个任务正在运行,而另一个任务被触发但需要忽略,则该任务不会被添加到执行队列中。

任务队列过滤器

首先,我们定义一个任务队列过滤器,用于在任务进入队列之前检查其执行条件。

#include <QObject>
#include <QQueue>
#include <QMutex>
#include <QRunnable>
#include <QThreadPool>
#include <QDebug>class TaskQueueFilter : public QObject{Q_OBJECTpublic:void addTask(QRunnable *task, const QString &taskName) {QMutexLocker locker(&mutex);// 检查任务是否可以执行if (taskName == "B" && isTaskRunning("A")){qDebug() << "Task" << taskName << "is ignored because Task A is running.";delete task;  // 忽略任务B,不将其添加到队列中return;}// 添加任务到队列并执行queue.enqueue(task);QThreadPool::globalInstance()->start(task);qDebug() << "Task" << taskName << "is added to the queue.";}void setTaskRunning(const QString &taskName, bool running) {QMutexLocker locker(&mutex);runningTasks[taskName] = running;}private:bool isTaskRunning(const QString &taskName) {return runningTasks.value(taskName, false);}QQueue<QRunnable*> queue;QMap<QString, bool> runningTasks;QMutex mutex;
};

任务类定义

我们定义两个任务 A 和 B。任务 A 运行时,任务 B 被触发将被忽略。

class TaskA : public QRunnable{
public:TaskA(TaskQueueFilter *filter): filter(filter) {}void run() override{filter->setTaskRunning("A", true);qDebug() << "Task A started";QThread::sleep(5);  // 模拟任务A的耗时操作qDebug() << "Task A finished";filter->setTaskRunning("A", false);}private:TaskQueueFilter *filter;
};class TaskB : public QRunnable{
public:TaskB(TaskQueueFilter *filter): filter(filter) {}void run() override{qDebug() << "Task B started";QThread::sleep(2);  // 模拟任务B的耗时操作qDebug() << "Task B finished";}private:TaskQueueFilter *filter;
};

主程序

在主程序中,我们初始化任务过滤器并启动任务 A 和任务 B。任务 B 会在任务 A 运行期间被忽略。

#include <QCoreApplication>
#include <QThreadPool>int main(int argc, char *argv[]) 
{QCoreApplication app(argc, argv);TaskQueueFilter filter;TaskA *taskA = new TaskA(&filter);TaskB *taskB = new TaskB(&filter);filter.addTask(taskA, "A");// 模拟任务B在任务A运行期间被触发QThread::sleep(1);filter.addTask(taskB, "B");return app.exec();
}

特点

  • 任务提交前检查: 在任务被添加到队列之前,检查其是否满足执行条件。只有满足条件的任务才会被添加到执行队列中。
  • 预防性: 在任务进入队列前就进行过滤,防止不必要的任务进入执行队列。
  • 队列集中管理: 任务的添加和执行由队列统一管理,适合集中化任务调度的场景。

优点

  • 资源节约: 不会启动不必要的任务,从而节约系统资源,避免浪费。
  • 集中控制: 所有任务的状态检查和过滤都集中在任务队列中,易于管理和扩展。
  • 效率高: 任务在进入队列前就被过滤掉,避免了不必要的执行准备工作。

缺点

  • 灵活性较低: 由于检查是在任务提交前进行的,如果任务状态在提交后发生变化,任务可能无法响应这些变化。
  • 需要额外的队列管理逻辑: 需要维护一个任务队列和状态检查的逻辑,增加了系统复杂性。

适用场景

  • 适用于任务量大且需要集中调度和管理的场景。
  • 适用于任务状态在任务提交前确定,不需要在任务执行过程中动态检查的场景。

6.3 基于信号与槽的任务忽略

使用信号与槽机制,可以在任务被触发时动态检查并决定是否执行任务。

任务控制器

任务控制器负责接收任务的启动信号,并决定是否忽略任务。

#include <QObject>
#include <QRunnable>
#include <QThreadPool>
#include <QDebug>class TaskController : public QObject 
{Q_OBJECTpublic:void startTask(const QString &taskName) {if (taskName == "B" && isTaskARunning){qDebug() << "Task B is ignored because Task A is running.";return;}if (taskName == "A") {isTaskARunning = true;}// 创建任务并启动QRunnable *task = createTask(taskName);if (task){QThreadPool::globalInstance()->start(task);qDebug() << "Task" << taskName << "started.";}}void setTaskAState(bool running){isTaskARunning = running;}signals:void taskFinished(const QString &taskName);private:QRunnable* createTask(const QString &taskName){if (taskName == "A") {return new TaskA(this);} else if (taskName == "B") {return new TaskB(this);}return nullptr;}bool isTaskARunning = false;
};

任务类定义

任务 A 完成后会发送一个信号通知任务控制器更新状态。

class TaskA : public QRunnable{
public:TaskA(TaskController *controller): controller(controller) {}void run() override {qDebug() << "Task A started";QThread::sleep(5);  // 模拟任务A的耗时操作qDebug() << "Task A finished";controller->setTaskAState(false);emit controller->taskFinished("A");}private:TaskController *controller;
};class TaskB : public QRunnable{
public:TaskB(TaskController *controller): controller(controller) {}void run() override{qDebug() << "Task B started";QThread::sleep(2);  // 模拟任务B的耗时操作qDebug() << "Task B finished";}
private:TaskController *controller;
};

主程序

在主程序中,我们连接信号与槽,并启动任务 A 和任务 B。任务 B 会在任务 A 运行时被忽略。

#include <QCoreApplication>int main(int argc, char *argv[]){QCoreApplication app(argc, argv);TaskController controller;QObject::connect(&controller, &TaskController::taskFinished, [&controller](const QString &taskName) {if (taskName == "A") {controller.setTaskAState(false);}});controller.startTask("A");// 模拟任务B在任务A运行期间被触发QThread::sleep(1);controller.startTask("B");return app.exec();
}

特点

  • 事件驱动: 基于信号与槽机制,任务的执行和忽略由信号触发的逻辑决定。
  • 动态响应: 当任务状态或条件发生变化时,可以通过信号立即触发相应的处理逻辑。
  • 松耦合: 信号与槽机制使得任务之间的依赖关系松耦合,易于扩展和修改。

优点

  • 实时响应: 当任务状态或条件发生变化时,可以立即通过信号通知其他任务,实现实时控制。
  • 灵活性高: 信号与槽机制使得任务可以灵活响应不同的事件或条件,适应性强。
  • 易于扩展: 添加新任务或修改任务逻辑时,只需调整信号与槽的连接方式,方便扩展和维护。

缺点

  • 复杂性增加: 需要管理信号与槽的连接关系,增加了系统的复杂性,尤其是在大型系统中,信号与槽的管理可能变得复杂。
  • 调试难度较大: 由于信号与槽机制是事件驱动的,调试和追踪任务的执行路径可能变得困难。

适用场景

  • 适用于任务之间存在复杂依赖关系或需要动态响应状态变化的场景。
  • 适用于系统中任务数量较多且需要灵活管理和扩展的场景。

对比总结

机制特点优点缺点适用场景
任务状态检查机制每个任务在执行时独立检查状态易于实现、独立性强、实时判断随任务数量增加管理复杂性增加、可能资源浪费任务数量少、逻辑简单
任务队列过滤机制任务提交前进行检查,未满足条件任务被过滤资源节约、集中控制、效率高灵活性低、需要额外队列管理逻辑任务量大、集中调度
基于信号与槽的任务忽略通过信号与槽机制动态决定任务执行或忽略实时响应、灵活性高、易于扩展系统复杂性增加、调试难度大复杂依赖关系、任务多且需要灵活管理

选择建议

  • 任务状态检查机制: 如果你的系统中任务相对独立,并且任务数量较少,使用任务状态检查机制更为简单和直观。它特别适合需要在任务运行过程中动态判断的情况。

  • 任务队列过滤机制: 如果你的系统有大量任务需要集中管理,并且任务状态在提交前可以确定,任务队列过滤机制更为高效。它能够在任务进入队列前进行过滤,节省资源。

  • 基于信号与槽的任务忽略: 如果你的系统任务之间有复杂的依赖关系,或者需要灵活响应不同的事件或条件,使用基于信号与槽的任务忽略机制是最合适的。这种机制的实时响应能力和扩展性是它的最大优势。

7. 总结

通过引入 TaskDependencyManager 来管理任务之间的依赖关系和状态,可以确保任务按照预期的顺序执行,并防止在不适当的情况下启动某些任务。这种设计特别适合处理复杂的任务调度场景,例如需要确保多个任务之间的顺序性或互斥性的应用程序。

  • 任务状态管理: 通过任务状态管理,确保任务在合适的时间点启动或完成。
  • 互斥锁和条件变量: 使用 QMutex 和 QWaitCondition 来保护共享资源,并协调任务的启动和完成。
  • 灵活性: 该架构允许轻松定义和调整任务之间的依赖关系,适应各种复杂的任务调度需求。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 二十五、go语言的通道
  • 代码随想录算法训练营第32天|509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯
  • 8.28路虎女事件
  • 掌握 JavaScript 解构赋值的指南
  • 蜜罐网络MHN安装过程中的坑
  • Pytorch实现多层LSTM模型,并增加emdedding、Dropout、权重共享等优化
  • Windows 下载安装RabbitMQ
  • 干货分享:推荐四大在线翻译神器!
  • 26. 在集合中删除元素时,为什么使用Iterator.remove()而不是Collection.remove()?
  • GeoScene Pro教程(004):GeoScene Pro制作与使用矢量切片包
  • STL之string
  • 技术指南:5分钟零成本实现本地AI知识库搭建
  • vue3+vant4父组件点击提交并校验子组件form表单
  • 【区块链 + 司法存证】优证云:基于 FISCO BCOS 的存证平台 | FISCO BCOS应用案例
  • 【Python自动化办公】复制Excel数据:将各行分别重复指定次数
  • canvas 绘制双线技巧
  • Centos6.8 使用rpm安装mysql5.7
  • dva中组件的懒加载
  • echarts的各种常用效果展示
  • Javascript Math对象和Date对象常用方法详解
  • Java知识点总结(JavaIO-打印流)
  • Laravel5.4 Queues队列学习
  • Linux编程学习笔记 | Linux IO学习[1] - 文件IO
  • October CMS - 快速入门 9 Images And Galleries
  • SpiderData 2019年2月13日 DApp数据排行榜
  • unity如何实现一个固定宽度的orthagraphic相机
  • vue学习系列(二)vue-cli
  • 从输入URL到页面加载发生了什么
  • 机器学习中为什么要做归一化normalization
  • 浅析微信支付:申请退款、退款回调接口、查询退款
  • MyCAT水平分库
  • 测评:对于写作的人来说,Markdown是你最好的朋友 ...
  • 新年再起“裁员潮”,“钢铁侠”马斯克要一举裁掉SpaceX 600余名员工 ...
  • ​比特币大跌的 2 个原因
  • ​如何使用ArcGIS Pro制作渐变河流效果
  • ​如何在iOS手机上查看应用日志
  • (10)Linux冯诺依曼结构操作系统的再次理解
  • (16)UiBot:智能化软件机器人(以头歌抓取课程数据为例)
  • (27)4.8 习题课
  • (rabbitmq的高级特性)消息可靠性
  • (Ruby)Ubuntu12.04安装Rails环境
  • (STM32笔记)九、RCC时钟树与时钟 第一部分
  • (八)c52学习之旅-中断实验
  • (附源码)springboot金融新闻信息服务系统 毕业设计651450
  • (力扣)循环队列的实现与详解(C语言)
  • (十六)Flask之蓝图
  • (四)activit5.23.0修复跟踪高亮显示BUG
  • (一)插入排序
  • (原創) 未来三学期想要修的课 (日記)
  • (转)Android中使用ormlite实现持久化(一)--HelloOrmLite
  • .bat批处理(九):替换带有等号=的字符串的子串
  • .htaccess配置重写url引擎
  • .Net IOC框架入门之一 Unity
  • .net 按比例显示图片的缩略图
  • .net 调用php,php 调用.net com组件 --