用于根据传入命令选择和执行任务的模式/体系结构

时间:2018-05-07 12:19:42

标签: c++ multithreading events design-patterns notifications

通常情况下,我会通过以下内容解决此问题:

task = _taskProvider.getNextFromQueue();

if (task.id == TaskExecutor.TasksEnum.One) {
    _taskExecutor.executeTaskOne();
    return;
} else if (task.id == TaskExecutor.TasksEnum.Two) {
    _taskExecutor.executeTaskTwo();
    return;
} else if (task.id == TaskExecutor.TasksEnum.Three) {
    _taskExecutor.executeTaskThree();
    return;
}
//and so on...

如果需要,我可以将if-else切换为switch。 我相信存在一种更好的方法来实现类似的代码。 我想到的一件事是使用表(map)存储任务ID和指向相应函数的指针,但我不确定它是否有用并提供足够的性能。

另一件令我担忧的事情是通知机制。当TaskProviderTaskExecutor是在不同线程上运行的两个独立实体时,会发生此问题。如果几个动作需要一些时间来完成执行,那就更糟了,我需要创建一个逻辑来让调用者等待结果。

例如,如果executeTaskTwo()需要一些时间才能完成,我会在后台线程中执行它,这意味着executeTaskTwo()几乎会立即返回,而任务本身仍然会执行。但是一旦结束,我如何通知呼叫者它的完成情况?

我猜这个解决方案涉及大量修改,也许是一些事件库(我之前从未使用过这些)。我需要一个起点。我猜有某种模式,但我不知道究竟是哪一种。

更新1:

有关一般架构和问题的更多信息 有三个实体:

  • Hardware - 管理硬件​​信号(输入,输出)。只关心硬件,不问问为什么?
  • Network - 管理与设备的远程连接。只关心数据传输。同样,它并不关心数据代表什么。
  • Controller - 控制实际设备的任务/算法(此设备的功能)。这是设备的大脑。

每个实体都在自己的线程上运行。有时,事件发生,一个实体需要将此事件发送给另一个实体(或所有实体)。例如,来自network的命令打开一些由硬件管理的LED,或者,应该向network(通知远程客户端)和controller发出无法转动此LED的信号。 (执行紧急停止)。

现在每个实体(类)都有一组函数(方法),其他实体在这个实体执行某些操作时会调用这些函数。如果被调用的函数需要时间来执行,则调用者实体被阻塞,这是不好的。产生处理每个命令(或事件)的后台线程也感觉不对。 现在每个实体都拥有自己的命令队列并按顺序执行它的命令(execute(queue.getNext())),但它无效,因为某些命令执行起来很快,而其他命令需要时间和资源。

2 个答案:

答案 0 :(得分:1)

  

我相信有更好的方法来实现类似的代码。

switch语句就像你能得到的一样好。它(通常)会在您的机器代码中生成一个跳转表,该表将直接将程序计数器发送到该位置以继续执行。

使用map(红黑树)存储idspointers to functions,效果不如switch中的完美哈希效率高,可能会阻止内联优化

  

我会在后台线程中执行它,这意味着executeTaskTwo()几乎会立即返回。

我对多线程知之甚少,但你为什么要早点回来?

线程完成后,其余线程的线程是否为join

如果你的意思是主要线程继续做其他事情,那么你可以有一个std::atomic<bool>来告诉你这个过程是否已经完成。如果只有一个线程写入它(工作线程),而其他线程读取(主线程),则行为定义良好。

如果您不想存储一堆布尔值并且只想知道某个流程是否仍在运行,那么您可以使用计数器增量 在线程开始和结束时递减

答案 1 :(得分:0)

你能把Task作为基类,让`getNextFromQueue()返回一个派生自它的具体类吗?这是一个简单的代码,可以给出想法/

class Task
{
public:
void execute() = 0;

}

class TaskOne : public Task
{
public:
void execute()
  {
     // execute the task here
  }
}

所以你的代码看起来像是:

Task * task = _taskProvider.getNextFromQueue();

if (task)
   task->execute()