Qt调度 程序

存在 三个程序A,B,C,C程序依赖程序A和B,当程序A或B终止时,程序C必须重新启动,当程序C终止时,只需要重新启动程序C

scheduler.h

复制代码
/**
 * @file scheduler.h
 * @brief 程序调度器类,用于管理TestA、TestB和TestC三个程序的启动、监控和重启
 * 
 * 功能说明:
 * 1. 启动程序C之前,必须确保程序A和程序B都在运行
 * 2. 如果程序C挂掉,需要重新启动程序A和程序B,然后再启动程序C
 * 3. 如果程序A或B挂掉,必须停止程序C,然后重新启动挂掉的程序,再启动程序C
 */

#ifndef SCHEDULER_H
#define SCHEDULER_H

#include <QObject>
#include <QProcess>
#include <QTimer>
#include <QString>
#include <QDir>

/**
 * @class Scheduler
 * @brief 程序调度器类
 * 
 * 负责管理三个程序的启动顺序、监控运行状态和处理崩溃重启逻辑
 */
class Scheduler : public QObject
{
    Q_OBJECT

public:
    /**
     * @brief 构造函数
     * @param parent 父对象指针
     */
    explicit Scheduler(QObject *parent = nullptr);
    
    /**
     * @brief 启动调度器
     * 初始化并启动所有被管理的程序
     */
    void start();

private slots:
    /**
     * @brief 程序A结束时的回调函数
     * @param exitCode 退出代码
     * @param exitStatus 退出状态(正常退出或崩溃)
     */
    void onProcessAFinished(int exitCode, QProcess::ExitStatus exitStatus);
    
    /**
     * @brief 程序B结束时的回调函数
     * @param exitCode 退出代码
     * @param exitStatus 退出状态(正常退出或崩溃)
     */
    void onProcessBFinished(int exitCode, QProcess::ExitStatus exitStatus);
    
    /**
     * @brief 程序C结束时的回调函数
     * @param exitCode 退出代码
     * @param exitStatus 退出状态(正常退出或崩溃)
     */
    void onProcessCFinished(int exitCode, QProcess::ExitStatus exitStatus);
    
    /**
     * @brief 程序A发生错误时的回调函数
     * @param error 错误类型
     */
    void onProcessAError(QProcess::ProcessError error);
    
    /**
     * @brief 程序B发生错误时的回调函数
     * @param error 错误类型
     */
    void onProcessBError(QProcess::ProcessError error);
    
    /**
     * @brief 程序C发生错误时的回调函数
     * @param error 错误类型
     */
    void onProcessCError(QProcess::ProcessError error);
    
    /**
     * @brief 定期检查进程状态
     * 由定时器触发,每秒执行一次,确保进程依赖关系正确
     */
    void checkProcesses();

private:
    /**
     * @brief 启动程序A
     */
    void startProcessA();
    
    /**
     * @brief 启动程序B
     */
    void startProcessB();
    
    /**
     * @brief 启动程序C
     * 只有在程序A和程序B都在运行时才会启动
     */
    void startProcessC();
    
    /**
     * @brief 停止程序C
     */
    void stopProcessC();
    
    /**
     * @brief 检查进程是否正在运行
     * @param process 要检查的进程指针
     * @return 如果进程正在运行返回true,否则返回false
     */
    bool isProcessRunning(QProcess *process);
    
    /**
     * @brief 获取可执行文件的完整路径
     * @param programName 程序名称(如"TestA")
     * @return 可执行文件的完整路径
     */
    QString getExecutablePath(const QString &programName);

    QProcess *processA;      ///< 程序A的进程对象
    QProcess *processB;      ///< 程序B的进程对象
    QProcess *processC;      ///< 程序C的进程对象
    QTimer *checkTimer;      ///< 用于定期检查进程状态的定时器
    bool isRestarting;       ///< 标记是否正在重启过程中,用于防止重复重启
};

#endif // SCHEDULER_H

scheduler.cpp

复制代码
/**
 * @file scheduler.cpp
 * @brief 程序调度器实现文件
 */

#include "scheduler.h"
#include <QDebug>
#include <QCoreApplication>
#include <QDir>
#include <QFileInfo>

/**
 * @brief 构造函数
 * 初始化所有进程对象、连接信号槽、启动定时器
 */
Scheduler::Scheduler(QObject *parent)
    : QObject(parent)
    , processA(nullptr)
    , processB(nullptr)
    , processC(nullptr)
    , isRestarting(false)
{
    // 创建三个进程对象
    processA = new QProcess(this);
    processB = new QProcess(this);
    processC = new QProcess(this);

    // 连接进程结束信号到对应的槽函数
    // 当进程正常退出或崩溃时会触发finished信号
    connect(processA, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
            this, &Scheduler::onProcessAFinished);
    connect(processB, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
            this, &Scheduler::onProcessBFinished);
    connect(processC, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
            this, &Scheduler::onProcessCFinished);

    // 连接进程错误信号到对应的槽函数
    // 当进程启动失败或其他错误时会触发errorOccurred信号
    connect(processA, &QProcess::errorOccurred, this, &Scheduler::onProcessAError);
    connect(processB, &QProcess::errorOccurred, this, &Scheduler::onProcessBError);
    connect(processC, &QProcess::errorOccurred, this, &Scheduler::onProcessCError);

    // 创建定时器,用于定期检查进程状态
    checkTimer = new QTimer(this);
    connect(checkTimer, &QTimer::timeout, this, &Scheduler::checkProcesses);
    checkTimer->start(1000); // 每秒检查一次进程状态
}

/**
 * @brief 启动调度器
 * 按照依赖关系启动所有程序:先启动A和B,等待它们启动后再启动C
 */
void Scheduler::start()
{
    qDebug() << "Scheduler started";
    
    // 首先启动程序A和B(它们是程序C的依赖)
    startProcessA();
    startProcessB();
    
    // 延迟2秒后检查A和B是否都已启动,如果都在运行,则启动C
    // 这样可以确保C启动时,A和B都已经准备就绪
    QTimer::singleShot(2000, this, [this]() {
        if (isProcessRunning(processA) && isProcessRunning(processB)) {
            startProcessC();
        }
    });
}

/**
 * @brief 获取可执行文件的完整路径
 * @param programName 程序名称(如"TestA"、"TestB"、"TestC")
 * @return 可执行文件的完整路径
 * 
 * 查找策略(按优先级):
 * 1. 在项目目录的build子目录中查找(适用于开发环境)
 * 2. 在当前可执行文件目录中查找(适用于部署环境)
 * 3. 使用相对路径(如果程序在系统PATH中)
 */
QString Scheduler::getExecutablePath(const QString &programName)
{
    QDir appDir(QCoreApplication::applicationDirPath());
    QDir projectDir = appDir;
    
    // 策略1:尝试在项目目录下查找
    // 假设可执行文件在:项目根目录/程序名/build/Desktop_Qt_5_15_2_MinGW_64_bit-Debug/程序名.exe
    projectDir.cdUp(); // 从build目录回到项目根目录
    //D:/work/tmp/Qt/TestScheduler/Scheduler/build/Desktop_Qt_5_15_2_MinGW_64_bit-Debug/TestA/build/Desktop_Qt_5_15_2_MinGW_64_bit-Debug/TestA.exe
    QString exePath = projectDir.absoluteFilePath(programName + "/build/Desktop_Qt_5_15_2_MinGW_64_bit-Debug/" + programName + ".exe");
    
    if (QFileInfo::exists(exePath)) {
        return exePath;
    }
    
    // 策略2:如果找不到,尝试在当前可执行文件所在目录查找
    exePath = appDir.absoluteFilePath(programName + ".exe");
    if (QFileInfo::exists(exePath)) {
        return exePath;
    }
    
    // 策略3:最后尝试相对路径(如果程序在系统PATH中,可以直接通过名称找到)
    return programName + ".exe";
}

/**
 * @brief 启动程序A
 * 如果程序A已经在运行,则直接返回,避免重复启动
 */
void Scheduler::startProcessA()
{
    // 如果程序A已经在运行,不需要重复启动
    if (isProcessRunning(processA)) {
        return;
    }
    
    // 获取程序A的可执行文件路径并启动
    //QString exePath = getExecutablePath("TestA");
    QString exePath="D:\\work\\tmp\\Qt\\TestScheduler\\TestA\\build\\Desktop_Qt_5_15_2_MinGW_64_bit-Debug\\debug\\TestA.exe";
    qDebug() << "Starting Process A:" << exePath;
    processA->start(exePath);
    
    // 等待进程启动,最多等待3秒
    if (!processA->waitForStarted(3000)) {
        qDebug() << "Failed to start Process A:" << processA->errorString();
    }
}

/**
 * @brief 启动程序B
 * 如果程序B已经在运行,则直接返回,避免重复启动
 */
void Scheduler::startProcessB()
{
    // 如果程序B已经在运行,不需要重复启动
    if (isProcessRunning(processB)) {
        return;
    }
    
    // 获取程序B的可执行文件路径并启动
    //QString exePath = getExecutablePath("TestB");
    QString exePath ="D:\\work\\tmp\\Qt\\TestScheduler\\TestB\\build\\Desktop_Qt_5_15_2_MinGW_64_bit-Debug\\debug\\TestB.exe";
    qDebug() << "Starting Process B:" << exePath;
    processB->start(exePath);
    
    // 等待进程启动,最多等待3秒
    if (!processB->waitForStarted(3000)) {
        qDebug() << "Failed to start Process B:" << processB->errorString();
    }
}

/**
 * @brief 启动程序C
 * 只有在程序A和程序B都在运行时才会启动C
 * 这是程序C的依赖要求
 */
void Scheduler::startProcessC()
{
    // 检查依赖关系:只有在A和B都在运行时才启动C
    if (!isProcessRunning(processA) || !isProcessRunning(processB)) {
        qDebug() << "Cannot start Process C: Both A and B must be running";
        return;
    }
    
    // 如果程序C已经在运行,不需要重复启动
    if (isProcessRunning(processC)) {
        return;
    }
    
    // 获取程序C的可执行文件路径并启动
    //QString exePath = getExecutablePath("TestC");
    QString exePath = "D:\\work\\tmp\\Qt\\TestScheduler\\TestC\\build\\Desktop_Qt_5_15_2_MinGW_64_bit-Debug\\debug\\TestC.exe";
    qDebug() << "Starting Process C:" << exePath;
    processC->start(exePath);
    
    // 等待进程启动,最多等待3秒
    if (!processC->waitForStarted(3000)) {
        qDebug() << "Failed to start Process C:" << processC->errorString();
    }
}

/**
 * @brief 停止程序C
 * 强制终止程序C的进程
 */
void Scheduler::stopProcessC()
{
    if (isProcessRunning(processC)) {
        qDebug() << "Stopping Process C";
        // 强制终止进程
        processC->kill();
        // 等待进程完全结束,最多等待3秒
        processC->waitForFinished(3000);
    }
}

/**
 * @brief 检查进程是否正在运行
 * @param process 要检查的进程指针
 * @return 如果进程存在且状态为Running返回true,否则返回false
 */
bool Scheduler::isProcessRunning(QProcess *process)
{
    return process && process->state() == QProcess::Running;
}

/**
 * @brief 程序A结束时的处理函数
 * @param exitCode 退出代码
 * @param exitStatus 退出状态(NormalExit正常退出 或 CrashExit崩溃)
 * 
 * 处理逻辑:
 * - 如果程序C正在运行,必须先停止C,然后重启A,最后再启动C
 * - 如果程序C没有运行,直接重启A即可
 */
void Scheduler::onProcessAFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
    qDebug() << "Process A finished, exit code:" << exitCode << "exit status:" << exitStatus;
    
    // 如果正在重启过程中,忽略本次事件,避免重复处理
    if (isRestarting) {
        return;
    }
    
    // 如果程序C正在运行,必须按照规则先停止C
    if (isProcessRunning(processC)) {
        isRestarting = true; // 标记正在重启,防止重复触发
        stopProcessC(); // 停止程序C
        
        // 延迟1秒后重启程序A
        QTimer::singleShot(1000, this, [this]() {
            startProcessA();
            // 再延迟2秒后,如果A和B都在运行,则启动C
            QTimer::singleShot(2000, this, [this]() {
                if (isProcessRunning(processA) && isProcessRunning(processB)) {
                    startProcessC();
                }
                isRestarting = false; // 重启完成,清除标记
            });
        });
    } else {
        // 程序C没有运行,直接重启A即可
        QTimer::singleShot(1000, this, [this]() {
            startProcessA();
        });
    }
}

/**
 * @brief 程序B结束时的处理函数
 * @param exitCode 退出代码
 * @param exitStatus 退出状态(NormalExit正常退出 或 CrashExit崩溃)
 * 
 * 处理逻辑:
 * - 如果程序C正在运行,必须先停止C,然后重启B,最后再启动C
 * - 如果程序C没有运行,直接重启B即可
 */
void Scheduler::onProcessBFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
    qDebug() << "Process B finished, exit code:" << exitCode << "exit status:" << exitStatus;
    
    // 如果正在重启过程中,忽略本次事件,避免重复处理
    if (isRestarting) {
        return;
    }
    
    // 如果程序C正在运行,必须按照规则先停止C
    if (isProcessRunning(processC)) {
        isRestarting = true; // 标记正在重启,防止重复触发
        stopProcessC(); // 停止程序C
        
        // 延迟1秒后重启程序B
        QTimer::singleShot(1000, this, [this]() {
            startProcessB();
            // 再延迟2秒后,如果A和B都在运行,则启动C
            QTimer::singleShot(2000, this, [this]() {
                if (isProcessRunning(processA) && isProcessRunning(processB)) {
                    startProcessC();
                }
                isRestarting = false; // 重启完成,清除标记
            });
        });
    } else {
        // 程序C没有运行,直接重启B即可
        QTimer::singleShot(1000, this, [this]() {
            startProcessB();
        });
    }
}

/**
 * @brief 程序C结束时的处理函数
 * @param exitCode 退出代码
 * @param exitStatus 退出状态(NormalExit正常退出 或 CrashExit崩溃)
 * 
 * 处理逻辑:
 * - 如果程序C挂掉,需要确保程序A和程序B都在运行
 * - 如果A或B没有运行,先启动它们
 * - 等待A和B都启动完成后,再重新启动C
 */
void Scheduler::onProcessCFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
    qDebug() << "Process C finished, exit code:" << exitCode << "exit status:" << exitStatus;
    
    // 如果正在重启过程中,忽略本次事件,避免重复处理
    if (isRestarting) {
        return;
    }
    
    isRestarting = true; // 标记正在重启,防止重复触发
    
    // 确保程序A和程序B都在运行(这是程序C的依赖要求)
    if (!isProcessRunning(processA)) {
        startProcessA();
    }
    if (!isProcessRunning(processB)) {
        startProcessB();
    }
    
    // 延迟3秒后,等待A和B启动完成,然后重新启动C
    QTimer::singleShot(3000, this, [this]() {
        // 检查A和B都在运行,满足C的依赖要求
        if (isProcessRunning(processA) && isProcessRunning(processB)) {
            startProcessC();
        }
        isRestarting = false; // 重启完成,清除标记
    });
}

/**
 * @brief 程序A发生错误时的处理函数
 * @param error 错误类型
 * 
 * 将错误转换为进程结束事件,统一由onProcessAFinished处理
 */
void Scheduler::onProcessAError(QProcess::ProcessError error)
{
    qDebug() << "Process A error:" << error;
    // 将错误视为崩溃退出,调用结束处理函数
    onProcessAFinished(-1, QProcess::CrashExit);
}

/**
 * @brief 程序B发生错误时的处理函数
 * @param error 错误类型
 * 
 * 将错误转换为进程结束事件,统一由onProcessBFinished处理
 */
void Scheduler::onProcessBError(QProcess::ProcessError error)
{
    qDebug() << "Process B error:" << error;
    // 将错误视为崩溃退出,调用结束处理函数
    onProcessBFinished(-1, QProcess::CrashExit);
}

/**
 * @brief 程序C发生错误时的处理函数
 * @param error 错误类型
 * 
 * 将错误转换为进程结束事件,统一由onProcessCFinished处理
 */
void Scheduler::onProcessCError(QProcess::ProcessError error)
{
    qDebug() << "Process C error:" << error;
    // 将错误视为崩溃退出,调用结束处理函数
    onProcessCFinished(-1, QProcess::CrashExit);
}

/**
 * @brief 定期检查进程状态
 * 
 * 由定时器每秒触发一次,用于:
 * 1. 检查程序C的依赖关系:如果C在运行但A或B不在运行,停止C
 * 2. 检查程序C是否可以启动:如果C没有运行但A和B都在运行,尝试启动C
 * 
 * 这是一个安全机制,用于处理可能遗漏的边缘情况
 */
void Scheduler::checkProcesses()
{
    // 检查1:如果程序C正在运行,但程序A或程序B不在运行
    // 这违反了依赖关系,必须停止程序C
    if (isProcessRunning(processC)) {
        if (!isProcessRunning(processA) || !isProcessRunning(processB)) {
            qDebug() << "A or B is not running, stopping C";
            stopProcessC();
        }
    }
    
    // 检查2:如果程序C没有运行,但程序A和程序B都在运行
    // 且当前不在重启过程中,则尝试启动程序C
    if (!isProcessRunning(processC) && !isRestarting) {
        if (isProcessRunning(processA) && isProcessRunning(processB)) {
            startProcessC();
        }
    }
}

main.cpp

复制代码
#include "scheduler.h"
#include <QCoreApplication>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    Scheduler scheduler;
    scheduler.start();
    return a.exec();
}

运行截图:

相关推荐
智算菩萨2 小时前
【实战】使用讯飞星火API和Python构建一套文本摘要UI程序
开发语言·python·ui
A24207349302 小时前
JavaScript图表制作:从入门到精通
开发语言·javascript·信息可视化
明飞19872 小时前
QT笔记1
qt
林政硕(Cohen0415)2 小时前
ARM Qt 字体过小的问题
arm开发·qt
BD_Marathon2 小时前
Vue3_简介和快速体验
开发语言·javascript·ecmascript
Poetinthedusk2 小时前
设计模式-命令模式
windows·设计模式·c#·wpf·命令模式
tryxr3 小时前
Java 多线程标志位的使用
java·开发语言·volatile·内存可见性·标志位
暗然而日章3 小时前
C++基础:Stanford CS106L学习笔记 13 特殊成员函数(SMFs)
c++·笔记·学习
追烽少年x3 小时前
Qt中构建多语言程序
qt