繁体   English   中英

事件驱动的模拟类

[英]Event-driven simulation class

我正在完成Bjarne Stroustrup的C ++编程语言中的一些练习。 我对第12章末尾的问题11感到困惑:

(* 5)设计并实现用于编写事件驱动模拟的库。 提示:<task.h>。 ...类任务的对象应该能够保存其状态并恢复该状态,以便它可以作为协程运行。 可以将特定任务定义为从任务派生的类的对象。 由任务执行的程序可以被定义为虚拟功能。 ......应该有一个实现虚拟时间概念的调度程序。 ......任务需要沟通。 为此设计一个类队列。 ...

我不确定这究竟是什么要求。 任务是一个单独的线程吗? (据我所知,没有系统调用就不可能创建新的线程,因为这是一本关于C ++的书,我不相信这是意图。)没有中断,如何启动和停止运行功能? 我假设这将涉及忙等待(也就是说,连续循环并检查条件),虽然我看不出如何将其应用于可能不会终止一段时间的函数(例如,如果它包含无限循环) 。

编辑:请参阅下面的帖子了解更多信息。

以下是我对“事件驱动模拟”的理解:

  • 控制器处理事件队列,调度在特定时间发生的事件,然后在队列上执行top事件。
  • 活动在预定时间瞬间完成。 例如,“移动”事件将更新模拟中实体的位置和状态,使得状态向量在当前模拟时间有效。 “感知”事件必须确保所有实体的状态都在当前时间,然后使用一些数学模型来评估当前实体能够感知其他实体的程度。 (想想机器人在板上四处走动。)
  • 因此,时间不连续,从事件跳到事件。 将其与时间驱动的模拟进行对比,其中时间以离散步骤移动,并且每个时间步骤更新所有实体的状态(大多数Simulink模型)。
  • 然后事件可以以其自然速率发生。 在模拟中以最精确的速率重新计算所有数据通常没有意义。

大多数生产事件驱动的模拟都在一个线程中运行。 它们本质上可能很复杂,因此尝试同步多线程模拟往往会增加指数层的复杂性。 话虽如此,有一个称为分布交互式仿真 (DIS)的多进程军事模拟标准,它使用预定义的TCP消息在进程之间传输数据。

编辑:定义建模和模拟之间的差异非常重要。 模型是系统或过程的数学表示。 模拟是根据一段时间内执行的一个或多个模型构建的。 同样,事件驱动的模拟在事件之间跳跃,而时间驱动的模拟在恒定的时间步骤进行。

提示:<task.h>。

是对早期版本的CFront附带的旧的合作多任务库的引用 (您也可以在该页面下载)。

如果您阅读论文“ 用于协同例程样式编程的一套C ++类 ”,那么事情会更有意义。


添加一点:

我不是一个足够的程序员来使用任务库。 但是,我知道C ++是在Stroustrup在Simula中编写一个具有许多与任务库相同属性的模拟之后设计的,所以我一直很好奇它。

如果我要从书中实现练习,我可能会这样做(请注意,我没有测试过这段代码,甚至尝试编译它):

class Scheduler {
    std::list<*ITask> tasks;
  public:
    void run()
    {
        while (1) // or at least until some message is sent to stop running
            for (std::list<*ITask>::iterator itor = tasks.begin()
                      , std::list<*ITask>::iterator end = tasks.end()
                    ; itor != end
                    ; ++itor)
                (*itor)->run(); // yes, two dereferences
    }

    void add_task(ITask* task)
    {
        tasks.push_back(task);
    }
};

struct ITask {
    virtual ~ITask() { }
    virtual void run() = 0;
};

我知道人们会不同意我的一些选择。 例如,使用接口的结构; 但是结构体具有从默认继承它们的行为是公共的(默认情况下从类继承是私有的),并且我没有看到从接口私下继承的任何值,那么为什么不将公共继承作为默认值呢?

这个想法是调用ITask :: run()将阻塞调度程序,直到任务到达可以被中断的点,此时任务将从run方法返回,并等待调度程序调用再次运行到继续。 “合作多任务”中的“合作”意味着“任务说明何时可以被打断”(“协程”通常意味着“合作多任务处理”)。 一个简单的任务可能只在其run()方法中做一件事,一个更复杂的任务可能实现一个状态机,并可能使用其run()方法来确定该对象当前处于什么状态并调用其他方法在那个州。 任务必须暂时放弃控制才能实现,因为这是“合作多任务处理”的定义。 这也是所有现代操作系统不使用协作式多任务处理的原因。

此实现不会(1)遵循公平调度(可能保持在任务的run()方法中花费的时钟滴答的总计,并跳过相对于其他任务使用了太多时间的任务,直到其他任务“赶上”) ,(2)允许删除任务,甚至(3)允许停止调度程序。

至于任务之间的通信,您可以考虑查看Plan 9的libtaskRob Pike的newsqueak获取灵感(“UNIX实现Newsqueak”下载包括一篇论文,“The News of Newsqueak”,讨论在一个有趣的虚拟机中传递消息)。

但我相信这是Stroustrup想到的基本骨架。

听起来像是练习要求你实施一个合作的多任务调度程序。 调度程序在虚拟时间内运行(您在任何级别定义/实现的时间标记),选择基于队列运行的任务(请注意,描述提到您需要实现一个),以及当前任务是完成后,调度程序选择下一个并开始运行。

离散事件模拟的广义结构基于键入时间值的优先级队列。 从广义上讲,它就像:

While (not end condition):
        Pop next event (one with the lowest time) from the priority queue
        Process that event, which may generate more events
        If a new event is generated:
            Place this on the priority queue keyed at its generated time

协同例程将模型的视图从以事件为中心变为以实体为中心。 实体可以经历一些生命周期(例如,接受作业,获取资源X,处理作业,释放资源X,将作业放入队列中以进行下一步)。 由于使用类似信号量的同步原语处理抓取资源,因此编程更容易一些。 作业和同步原语生成事件并在后台对它们进行排队。

这给出了一个概念上类似于操作系统中的进程的模型,以及当它的请求输入或共享资源可用时唤醒进程的调度程序。 协同常规模型使模拟更容易理解,这对于模拟复杂系统很有用。

(我不是C ++开发者)

可能意味着你需要创建一个类Task(如在Event中),它主要由回调函数指针和预定时间组成,并且可以存储在Scheduler类的列表中,而这些列表基本上应该保留跟踪时间计数器并在时间到达时调用每个任务的功能。 这些任务应该由模拟的对象创建。

如果您需要离散模拟方面的帮助,请继续编辑问题。

这是对于对于SottieT812的答案,对于他的评论。 它的评论太大了,所以我决定再做一个答案。

在模拟状态仅响应于事件而改变的意义上是事件驱动的。 例如,假设您有两个事件导弹发射导弹撞击 当执行启动事件时,它会确定它将影响的时间和地点,并在适当的时间安排影响事件。 导弹的位置不是在发射和撞击之间计算的,尽管它可能有一种方法可以被其他物体调用以在特定时间获得位置。

这与时间驱动模拟形成对比,时间驱动模拟在每个时间步长(例如1秒)之后计算导弹(以及模拟中的每个其他对象)的精确位置。

根据模型的特征,所需答案的保真度以及许多其他因素,事件驱动或时间驱动模拟可能表现更好。

编辑:如果有兴趣了解更多信息,请查看冬季模拟大会的论文

有一本书和框架称为DEMOS (Simula上的离散事件建模),它描述了一个基于协同例程的框架(同名DEMOS)。 虽然30年左右的老DEMOS实际上是一个非常好的系统, Graham Birtwistle是一个非常好的人。

如果你在C ++上实现协同例程(想想setjump / longjump),你应该看看这本书,以获得真正优雅的离散事件建模框架的描述。 虽然它已有30年历史,但它仍然是一个永恒的经典,并且仍然拥有粉丝群。

在链接到“me.yahoo.com / ...”的论文中描述了task.h类:

  1. 任务并行执行
  2. 任务可能会暂停并稍后恢复

该库被描述为一种多道程序设计方法。

是否可以在不使用线程或单独进程的情况下执行此操作?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM