描述
我目前正在为 C# 多代理模拟设计一个架构,其中代理动作由其“大脑”中的许多模块驱动,这些模块可以读取传感器、投票支持动作或向其他模块发送消息/查询(所有这些都是通过消息交换)。当然,模块可以有状态。
模块并行运行:它们有一个更新方法,该方法使用消息和查询,并执行某种计算。update 方法返回迭代器,并且在它们的主体中有多个 yield,这样我就可以协同调度模块。我不会为每个模块使用单个线程,因为我希望每个代理都有数百到数千个模块,这将导致线程开销占用大量 RAM。
我希望这些模块的行为类似于运行时插件,以便在模拟运行时我可以添加新模块类并重写/调试现有模块类,而无需停止模拟过程,然后使用这些类从代理的大脑,或者只是让现有模块由于其方法的新实现而改变其行为。
可能的解决方案
在过去的几天里,我想出了一些可能的解决方案,但都有一些令人失望的地方:
将我的模块编译成 DLL,将每个模块加载到不同的AppDomain 中,然后使用 AppDomain.CreateInstanceFromAndUnwrap() 来实例化模块,然后我会将其转换为某个 IModule 接口,在我的模拟和模块之间共享(并由每个模块类实现) . 该接口将只公开所有模块共有的 SendMessage、Update 和一些其他成员。
- 此解决方案的问题是 AppDomain 之间的调用比直接调用(在同一个 AppDomain 内)慢得多。
- 另外,我不知道 AppDomains 的开销,但我想它们不是免费的,所以拥有数千可能会成为问题。
为模块使用一些脚本语言,同时为底层引擎保留 C#,这样就不会加载/卸载程序集。相反,我将为每个模块的脚本语言托管一个执行上下文。
- 我主要担心的是我不知道一种大的脚本语言(如“python、lua、ruby、js 很大,Autoit 和 Euphoria 不是”)快速、可嵌入到 .NET并允许逐步执行(其中我需要为了执行模块执行的协作调度)。
- 对此的另一个担忧是,我想我必须为每个模块使用运行时上下文,这反过来会产生巨大的开销。
- 最后,我认为脚本语言可能会比 C# 慢,这会降低性能。
避免卸载程序集,而是以某种方式重命名/版本化它们,这样我就可以拥有大量不同的版本,然后为每种类型使用最新的版本。
- 我什至不确定这是可能的(由于共同的类型和命名空间)
- 即使可能,它的内存效率也会非常低。
透明地重新启动模拟,这意味着暂停模拟(以及执行大脑/模块的调度程序),序列化所有内容(包括每个模块),退出模拟,重新编译代码,再次启动模拟,反序列化所有内容,捕获由于我对类所做的更改和恢复执行而引发的任何异常。
- 这是很多工作,所以我认为这是我最后的手段。
- 此外,整个过程在某些时候会非常缓慢,具体取决于模块的数量及其大小,因此不切实际
我可以克服最后一个问题(解决方案 4 中的整个过程变得缓慢),方法是混合解决方案 3 和 4,加载许多带有某种形式的版本控制的程序集,并不时执行重新启动以清理混乱。然而,我更喜欢不会仅仅因为我在模块类中做了一个小改动而中断整个模拟的东西。
实际问题
所以这是我的问题:还有其他解决方案吗?我是否错过了针对我发现的问题的任何解决方法?例如,是否有一些 .NET 脚本语言可以满足我的需求(解决方案 #2)?版本控制是否可能,以我模糊描述的方式(解决方案#3)?
甚至,更简单地说:.NET 是这个项目的错误平台吗?(我想坚持使用它,因为 C# 是我的主要语言,但如果需要,我可以看到自己在 Python 或类似的东西中这样做)