.NET多代理仿真的插件架构(运行时加载/卸载)

时间:2014-01-22 02:02:28

标签: c# .net plugins concurrency multi-agent

描述

我目前正在设计一个用于C#多智能体仿真的架构,其中代理操作由其“大脑”中的许多模块驱动,这些模块可以读取传感器,投票给某个动作或向其他模块发送消息/查询(所有这些)通过交换消息实现)。 当然,模块可以有一个状态。

模块以并行运行:它们有一个更新方法,它使用消息和查询,并执行某种计算。 update方法返回迭代器,并且在它们的主体中有多个yield,这样我就可以协同调度模块。我不为每个模块使用单个线程,因为我希望每个代理都有数百到数千个模块,这会导致线程开销占用大量RAM。

我希望这些模块的行为类似于运行时插件,这样在模拟运行时我可以添加新模块类并重写/调试现有模块,而无需停止模拟过程,然后使用这些类从代理的大脑中添加和删除模块,或者只是让现有的模块由于其方法的新实现而改变其行为。

可能的解决方案

我在过去几天提出了许多可能的解决方案,这些解决方案都令人失望:

  1. 将我的模块编译成DLL,将每个模块加载到不同的 AppDomain 中,然后使用AppDomain.CreateInstanceFromAndUnwrap()来实例化模块,然后将其转换为某些IModule接口,在我的模拟和模块(并由每个模块类实现)。该接口将仅公开SendMessage,Update和一些其他成员,这些成员对所有模块都是通用的。

    • 此解决方案的问题在于AppDomains之间的调用比直接调用(在同一AppDomain中)慢得多。
    • 另外,我不知道AppDomains的开销,但我认为它们不是免费的,所以有成千上万可能会成为一个问题。
  2. 对模块使用一些脚本语言,同时保留底层引擎的C#,以便没有程序集加载/卸载。相反,我会为每个模块托管脚本语言的执行上下文。

    • 我主要担心的是我不知道的脚本语言(如'python,lua,ruby,js很大,Autoit和Euphoria都不是')嵌入.NET 并允许逐步执行(我需要这样才能执行模块执行的协作调度)。
    • 关于这一点的另一个问题是,我想我必须为每个模块使用运行时上下文,这反过来会产生巨大的开销。
    • 最后,我认为脚本语言可能比C#慢,这会降低性能。
  3. 避免卸载程序集,而是以某种方式重命名/版本化它们,以便我可以拥有大量不同版本,然后只为每种类型使用最新版本。

    • 我甚至不确定这是否可行(由于类型和名称空间有限)
    • 即使可能,也会非常低效。
  4. 执行模拟的透明重启,这意味着暂停模拟(以及执行大脑/模块的调度程序),序列化所有内容(包括每个模块),退出模拟,重新编译代码,再次开始模拟,反序列化所有内容,捕获由于我对类所做的更改而引发的任何异常并恢复执行。

    • 这是很多工作,所以我认为这是我的最后一招。
    • 此外,根据模块数量及其尺寸,整个过程在某些时候会非常慢,使其变得不切实际
  5. 我可以克服这个最后一个问题(解决方案4中的整个过程变得缓慢),通过混合解决方案3和4,加载许多组件与某种形式的版本控制并执行重新启动以不时地清理混乱。然而,我更喜欢不会因为我在模块类中做了一些小改动而中断整个模拟的东西。

    实际问题

    所以这是我的问题:还有其他解决方案吗?我是否错过了我找到的问题的任何解决方法? 例如,是否有一些.NET脚本语言可以满足我的需求(解决方案#2)?版本控制是否可能,就像我模糊地描述它(解决方案#3)?

    甚至更简单:.NET是否是这个项目的错误平台? (我想坚持下去,因为C#是我的主要语言,但如果有必要,我可以看到自己在Python中做这件事等等)

2 个答案:

答案 0 :(得分:1)

答案 1 :(得分:0)

我正在以一种非常类似的方式工作的模拟系统,将代理模块视为插件。

我创建了一个插件管理器,它处理每个域加载相关的东西,检查虚拟域中的插件有效性,然后在引擎域中加载它。

使用AppDomain可以获得完全控制权,并且可以通过并行运行插件管理器的任务来缩短处理时间。

AppDomains不是免费的,但您可以只使用两个(或者如果您需要在验证域和执行域之间进行更多隔离,则​​可以使用三个)。

一旦验证了插件文件,您就可以随时在非常主要的进程中加载​​它,在任何域的探测路径中创建一个卷影副本(如果设置的话,在动态路径中创建),并将其定位而不是原始文件对于检查版本控制和更新。

使用域进行验证而另一个域执行可能需要交换上下文,后者在更新时负责以前的版本实例。

保持时间安排任务检查新插件和新版本,然后阻止插件模块使用,交换文件,重新加载和取消阻止,必要时重新启动之前的新版本。