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

工业软件架构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_OBJECT

public:
    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_OBJECT

public:
    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();

    // 启动任务A
    pool->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_OBJECT

public:
    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_OBJECT

public:
    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 来保护共享资源,并协调任务的启动和完成。
  • 灵活性: 该架构允许轻松定义和调整任务之间的依赖关系,适应各种复杂的任务调度需求。

http://www.kler.cn/a/284113.html

相关文章:

  • 深度学习和图像处理
  • 【AI日记】24.11.14 复习和准备 RAG 项目 | JavaScript RAG Web Apps with LlamaIndex
  • Java结合ElasticSearch根据查询关键字,高亮显示全文数据。
  • 什么时候需要复写hashcode()和compartTo方法
  • C++编程:利用环形缓冲区优化 TCP 发送流程,避免 Short Write 问题
  • 相机光学(四十二)——sony的HDR技术
  • 视频和音频合成视频Easy_Wav2Lip
  • ROS - Turtle Nest 使用说明
  • 机器学习/数据分析案例---糖尿病预测
  • Java基础——IO概述
  • Java算法之快速排序(Quick Sort)
  • 服务器机柜与网络机柜的区别有哪些?
  • 耦合和内聚
  • redis集群部署
  • 集成电路学习:什么是DAC数模转换器
  • Maven <parent> 标签的作用及使用详解
  • 【React】useEffect的使用场景与作用
  • 什么软件可以用平板远程控制电脑?
  • 【使用 Python 进行图像裁剪的多种方法】
  • Leetcode Hot 100刷题记录 -Day5(双指针)
  • 1.7 离散频率
  • python学习-04【流程控制语句】
  • Qt 调用MFC dll,动态库中有界面
  • 数据结构——链式二叉树的实现与分治编程思维(c语言实现)
  • sql-labs靶场(41-50)
  • unity脚本