工业软件架构 - 事件驱动 - 5
在一些复杂的系统中,任务需要暂停和继续运行功能。
实现带有暂停和继续功能的任务需要引入任务状态管理和线程同步机制。
常见的方式包括使用QWaitCondition和QMutex来控制任务的执行、暂停和继续。
设计思路
要实现任务的暂停和继续功能,可以考虑以下步骤:
- 任务状态管理: 使用一个状态变量来表示任务的当前状态,如“运行中”、“暂停中”等。
- 使用QWaitCondition和QMutex: 当任务需要暂停时,使用QWaitCondition让任务等待;当需要继续运行时,发送信号解除等待,任务继续执行。
- 任务控制器: 创建一个任务控制器来管理任务的状态和控制暂停、继续操作。
以常用的for循环任务举例。
任务类的实现
首先,定义一个任务类,该任务可以被暂停和继续。
#include <QRunnable>
#include <QWaitCondition>
#include <QMutex>
#include <QDebug>
class PausableTask : public QRunnable
{
public:
PausableTask() : isPaused(false), isStopped(false) {}
void run() override
{
qDebug() << "Task started";
for (int i = 0; i < 10; ++i)
{
QMutexLocker locker(&mutex);
// 检查是否被暂停
while (isPaused)
{
qDebug() << "Task paused at step" << i;
condition.wait(&mutex); // 等待继续信号
}
// 检查是否被停止
if (isStopped)
{
qDebug() << "Task stopped";
return;
}
qDebug() << "Task running, step" << i;
QThread::sleep(1); // 模拟任务的每一步耗时1秒
}
qDebug() << "Task completed";
}
void pause()
{
QMutexLocker locker(&mutex);
isPaused = true;
}
void resume()
{
QMutexLocker locker(&mutex);
isPaused = false;
condition.wakeAll(); // 解除暂停,继续执行
}
void stop()
{
QMutexLocker locker(&mutex);
isStopped = true;
condition.wakeAll(); // 如果任务被暂停,则解除等待以结束任务
}
private:
QMutex mutex;
QWaitCondition condition;
bool isPaused;
bool isStopped;
};
任务控制器
任务控制器负责启动、暂停、继续和停止任务。
class TaskController : public QObject
{
Q_OBJECT
public:
TaskController()
{
task = new PausableTask();
}
~TaskController()
{
delete task;
}
void startTask()
{
QThreadPool::globalInstance()->start(task);
}
void pauseTask()
{
task->pause();
}
void resumeTask()
{
task->resume();
}
void stopTask()
{
task->stop();
}
private:
PausableTask *task;
};
主程序
在主程序中,我们创建任务控制器,并通过命令来启动、暂停、继续和停止任务。
#include <QCoreApplication>
#include <QThreadPool>
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
TaskController controller;
// 启动任务
controller.startTask();
// 模拟任务暂停
QThread::sleep(3);
controller.pauseTask();
// 模拟任务继续
QThread::sleep(3);
controller.resumeTask();
// 等待任务完成
QThread::sleep(10);
return app.exec();
}
运行原理
- 启动任务: 任务启动后,开始执行每一步操作。
- 暂停任务: 当调用pauseTask()时,任务进入暂停状态并等待QWaitCondition的解除。
- 继续任务: 当调用resumeTask()时,任务解除等待,继续执行后续步骤。
- 停止任务: 如果在暂停状态下调用stopTask(),任务会直接结束。
扩展功能
- 任务的终止: 任务可以在暂停状态下被终止,确保任务不会继续执行。
- 状态反馈: 可以扩展任务类,使其能够通过信号槽机制反馈任务的当前状态,例如“正在运行”、“已暂停”、“已完成”等。
- 任务队列管理: 结合任务队列过滤机制,可以在任务进入队列前检查任务的状态,确保不会启动已经暂停或终止的任务。
总结
通过使用QWaitCondition和QMutex,你可以灵活地控制任务的执行、暂停和继续。这样的设计适用于需要动态管理任务状态的应用场景,如需要中断并稍后继续的长时间运行任务。
- 可暂停性: 任务可以在执行过程中被暂停,等待条件满足后再继续执行。
- 可恢复性: 任务在暂停后可以继续从暂停点执行,而无需重新启动任务。
- 线程安全: 使用QMutex确保状态检查和状态改变的线程安全,避免竞争条件。
非for循环任务
当任务不是通过 for 循环一行一行地执行,而是每一行代码执行不同功能,并且需要实现暂停和继续功能时,我们可以将任务分解成多个步骤,每个步骤对应任务的一部分逻辑。然后,通过在每个步骤之间插入状态检查和同步点来实现任务的暂停和继续功能。
任务分解与状态管理
将任务分解成多个步骤,每个步骤之间都检查是否需要暂停。如果任务被暂停,则任务将等待,直到收到继续执行的信号后,再执行下一个步骤。
实现思路
步骤划分:将任务按步骤划分,每个步骤单独执行一段功能逻辑。
状态管理:使用状态变量来跟踪任务是否应该暂停或继续运行。
同步机制:使用 QWaitCondition 和 QMutex 来实现任务的同步点,使得任务可以在暂停状态下等待,直到被唤醒继续执行。
任务类的实现
假设任务由多个步骤组成,每个步骤执行不同的功能。我们在每个步骤之间插入检查点。
#include <QRunnable>
#include <QWaitCondition>
#include <QMutex>
#include <QDebug>
class StepwiseTask : public QRunnable
{
public:
StepwiseTask() : isPaused(false), isStopped(false) {}
void run() override
{
qDebug() << "Task started";
step1();
checkPause(); // 检查是否需要暂停
if (isStopped) return;
step2();
checkPause(); // 检查是否需要暂停
if (isStopped) return;
step3();
checkPause(); // 检查是否需要暂停
if (isStopped) return;
qDebug() << "Task completed";
}
void pause()
{
QMutexLocker locker(&mutex);
isPaused = true;
}
void resume()
{
QMutexLocker locker(&mutex);
isPaused = false;
condition.wakeAll(); // 解除暂停,继续执行
}
void stop()
{
QMutexLocker locker(&mutex);
isStopped = true;
condition.wakeAll(); // 如果任务被暂停,则解除等待以结束任务
}
private:
void checkPause()
{
QMutexLocker locker(&mutex);
while (isPaused)
{
qDebug() << "Task paused";
condition.wait(&mutex); // 等待继续信号
}
}
void step1()
{
qDebug() << "Executing step 1";
// 执行步骤1的逻辑
QThread::sleep(1);
}
void step2()
{
qDebug() << "Executing step 2";
// 执行步骤2的逻辑
QThread::sleep(1);
}
void step3()
{
qDebug() << "Executing step 3";
// 执行步骤3的逻辑
QThread::sleep(1);
}
QMutex mutex;
QWaitCondition condition;
bool isPaused;
bool isStopped;
};
任务控制器
任务控制器用于启动、暂停、继续和停止任务。
class TaskController : public QObject
{
Q_OBJECT
public:
TaskController()
{
task = new StepwiseTask();
}
~TaskController()
{
delete task;
}
void startTask()
{
QThreadPool::globalInstance()->start(task);
}
void pauseTask()
{
task->pause();
}
void resumeTask()
{
task->resume();
}
void stopTask()
{
task->stop();
}
private:
StepwiseTask *task;
};
主程序
在主程序中,创建任务控制器,并通过命令来启动、暂停、继续和停止任务。
#include <QCoreApplication>
#include <QThreadPool>
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
TaskController controller;
// 启动任务
controller.startTask();
// 模拟任务暂停
QThread::sleep(2);
controller.pauseTask();
// 模拟任务继续
QThread::sleep(3);
controller.resumeTask();
// 等待任务完成
QThread::sleep(5);
return app.exec();
}
运行原理
- 启动任务: 任务启动后,按顺序执行步骤1、步骤2和步骤3。
- 暂停任务: 在任一步骤执行完毕后,任务检查是否被暂停。如果是,则任务进入暂停状态并等待继续信号。
- 继续任务: 当任务收到继续信号时,从暂停的位置继续执行下一个步骤。
扩展功能
- 更多步骤: 可以根据实际任务的需求添加更多步骤,每个步骤之间插入检查点,以确保任务的灵活性。
- 状态反馈: 通过信号槽机制反馈任务的当前状态给外部系统,提供任务执行的实时监控能力。
总结
- 分步骤执行: 将任务分解为多个步骤,每个步骤之间插入状态检查点,确保任务能够响应暂停和继续操作。
- 同步机制: 使用QWaitCondition和QMutex实现任务的暂停和继续功能,确保线程安全。
- 灵活性和扩展性: 这种设计允许你灵活地管理复杂任务,添加更多步骤,甚至根据实际需要调整任务的执行流程。
这种设计非常适合需要处理复杂逻辑的任务,尤其是那些需要在不同操作步骤之间支持暂停和继续功能的任务。它使得任务管理更加灵活和可控,适应性更强。
耗时任务继续运行
在工业领域中,有些任务继续运行不能简单的运行之后代码,而是需要从之前暂停的代码开始运行。
比如:电机运行任务中,任务目标是运行到目标点
O
1
,
O
2
,
O
3
O_1,O_2, O_3
O1,O2,O3;暂停任务是让电机运行停止;而继续运行任务是继续运动到运行到目标点
O
O
O。
#include <QRunnable>
#include <QDebug>
#include <QMutex>
class StepwiseTask : public QRunnable
{
public:
StepwiseTask() : isPaused(false), isStopped(false), currentStep(0) {}
void run() override
{
qDebug() << "Task started";
if(currentStep < 1)
{
step1();
if(checkPauseAndStop(0)) return;
}
if(currentStep < 2)
{
step2();
if(checkPauseAndStop(1)) return;
}
if(currentStep < 3)
{
step1();
if(checkPauseAndStop(2)) return;
}
qDebug() << "Task completed";
}
void pause()
{
QMutexLocker locker(&mutex);
isPaused = true;
stopMove(); // 轴运行停止
}
void reset()
{
QMutexLocker locker(&mutex);
currentStep = 0;
isStopped = false;
isPaused = false;
}
void stop()
{
QMutexLocker locker(&mutex);
isStopped = true;
stopMove(); // 轴运行停止
}
private:
bool checkPauseAndStop(const int& currentStep)
{
QMutexLocker locker(&mutex);
if (isStopped)
{
Reset();
return true;
}
else if (isPaused)
{
this->currentStep = currentStep ;
isPaused = false;
return true;
}
return false;
}
void step1()
{
Move2(O_1);
}
void step2()
{
Move2(O_2);
}
void step3()
{
Move2(O_3);
}
QMutex mutex;
int currentStep; // 当前步骤序号
bool isPaused;
bool isStopped;
};