我正在完成Bjarne Stroustrup的C ++编程语言中的一些练习。我对第12章末尾的问题11感到困惑:
(* 5)设计并实现用于编写事件驱动模拟的库。提示:< task.h>。 ...类任务的对象应该能够保存其状态并恢复该状态,以便它可以作为协程运行。可以将特定任务定义为从任务派生的类的对象。由任务执行的程序可以被定义为虚拟功能。 ......应该有一个实现虚拟时间概念的调度程序。 ......任务需要沟通。为此设计一个类队列。 ...
我不确定这究竟是什么要求。任务是一个单独的线程吗? (据我所知,没有系统调用就不可能创建新的线程,因为这是一本关于C ++的书,我不相信这是意图。)没有中断,如何启动和停止运行功能?我假设这将涉及忙等待(也就是说,连续循环并检查条件),虽然我看不出如何将其应用于可能不会终止一段时间的函数(例如,如果它包含无限循环)
编辑:请参阅下面的帖子,了解更多信息。
答案 0 :(得分:5)
以下是我对“事件驱动模拟”的理解:
大多数生产事件驱动的模拟都在一个线程中运行。它们本质上可能很复杂,因此尝试同步多线程模拟往往会增加指数层的复杂性。话虽如此,有一个称为Distributive Interactive Simulation(DIS)的多进程军事模拟标准,它使用预定义的TCP消息在进程之间传输数据。
编辑:定义建模和模拟之间的差异非常重要。模型是系统或过程的数学表示。模拟是根据一段时间内执行的一个或多个模型构建的。同样,事件驱动的模拟在事件之间跳跃,而时间驱动的模拟在恒定的时间步骤进行。
答案 1 :(得分:3)
提示:< task.h>。
是对early versions of CFront附带的旧的合作多任务库的引用(您也可以在该页面下载)。
如果您阅读了论文“A Set of C++ Classes for Co-routine Style Programming”,那么事情会更有意义。
添加一点:
我不是一个足够的程序员来使用任务库。但是,我知道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()方法中花费的时钟滴答的总计,并跳过相对于其他任务使用了太多时间的任务,直到其他任务“捕获” up“),(2)允许删除任务,甚至(3)允许调度程序停止。
至于任务之间的通信,您可以考虑查看Plan 9's libtask或Rob Pike's newsqueak获取灵感(“UNIX实现Newsqueak”下载包含一篇论文“The News of Newsqueak”,讨论消息传递在一个有趣的虚拟机中。)
但我相信这是Stroustrup想到的基本骨架。
答案 2 :(得分:2)
听起来像是练习要求你实施一个合作的多任务调度程序。调度程序在虚拟时间内运行(您在任何级别定义/实现的时间标记),选择基于队列运行的任务(请注意,描述提到您需要实现一个),以及当前任务是完成后,调度程序选择下一个并开始运行。
答案 3 :(得分:2)
离散事件模拟的广义结构基于键入时间值的优先级队列。从广义上讲,它就像:
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,将作业放入队列中以进行下一步)。由于使用类似信号量的同步原语处理抓取资源,因此编程更容易一些。作业和同步原语生成事件并在幕后排队。
这给出了一个模型,该模型在概念上类似于操作系统中的进程,并且当它的请求输入或共享资源可用时,调度程序唤醒进程。协同常规模型使模拟更容易理解,这对于模拟复杂系统非常有用。
答案 4 :(得分:1)
(我不是C ++ dev)
可能意味着你需要创建一个类Task(如在Event中),它主要由回调函数指针和预定时间组成,并且可以存储在Scheduler类的列表中,基本上应该跟踪时间计数器,并在时间到来时调用每个任务的功能。这些任务应该由模拟的对象创建。
如果您需要离散模拟方面的帮助,请继续编辑问题。
答案 5 :(得分:1)
这是对于Lithdecoy对SottieT812的回答的回应。它的评论太大了,所以我决定再做一个答案。
在模拟状态仅响应于事件而改变的意义上是事件驱动。例如,假设您有两个事件导弹发射和导弹影响。执行启动事件时,它会确定影响的时间和地点,并在适当的时间安排影响事件。导弹的位置不是在发射和撞击之间计算的,尽管它可能有一种方法可以被其他物体调用以在特定时间获得该位置。
这与时间驱动模拟形成对比,时间驱动模拟在每个时间步之后计算导弹(以及模拟中的每个其他物体)的准确位置,比如1秒。
根据模型的特性,所需答案的保真度以及许多其他因素,无论是事件驱动还是时间驱动模拟都可能表现得更好。
编辑:如果有人有兴趣了解更多信息,请查看Winter Simulation Conference
中的论文答案 6 :(得分:1)
有一本名为DEMOS的书籍和框架(Simula上的离散事件建模),它描述了一个基于协同例程的框架(同名DEMOS)。虽然30年左右的老DEMOS实际上是一个非常好的系统,Graham Birtwistle是一个非常好的人。
如果你在C ++上实现协同例程(想想setjump / longjump),你应该看一下本书中对真正优雅的离散事件建模框架的描述。虽然它已有30年历史,但它仍然是一个永恒的经典,并且仍然拥有粉丝群。
答案 7 :(得分:0)
在链接到“me.yahoo.com / ...”的论文中描述了task.h类:
该库被描述为一种多道程序设计方法。
是否可以在不使用线程或单独进程的情况下执行此操作?