存在 三个程序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();
}
运行截图:
