【问题标题】:Plugin architecture for .NET multi-agent simulation (runtime load/unload).NET 多代理模拟的插件架构(运行时加载/卸载)
【发布时间】:2014-01-22 02:02:28
【问题描述】:

描述

我目前正在为 C# 多代理模拟设计一种架构,其中代理操作由其“大脑”中的许多模块驱动,这些模块可以读取传感器、投票支持操作或向其他模块发送消息/查询(所有这些是通过消息交换实现的)。 当然,模块可以有状态。

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

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

可能的解决方案

在过去的几天里,我想出了一些可能的解决方案,但都有一些令人失望的地方:

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

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

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

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

    • 这是很多工作,所以我认为这是我的最后手段。
    • 此外,整个过程有时会非常缓慢,具体取决于模块的数量及其大小,因此不切实际

我可以克服最后一个问题(解决方案 4 中的整个过程变得缓慢),方法是混合解决方案 3 和 4,使用某种形式的版本控制加载许多程序集,并不时地执行重新启动以清理混乱。然而,我更喜欢不会仅仅因为我在模块类中做了一个小改动而中断整个模拟的东西。

实际问题

所以这是我的问题:还有其他解决方案吗?我是否错过了针对我发现的问题的任何解决方法? 例如,是否有一些 .NET 脚本语言可以满足我的需求(解决方案 #2)?是否可以按照我模糊描述的方式进行版本控制(解决方案 #3)?

或者更简单地说:.NET 是不是这个项目的错误平台? (我想坚持使用它,因为 C# 是我的主要语言,但如果需要,我可以看到自己在 Python 或类似的东西中这样做)

【问题讨论】:

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


    【解决方案1】:

    【讨论】:

    • 我现在这样做了,但似乎 MEF 将每个程序集加载到当前 AppDomain 中,这使得在不停止应用程序的情况下无法卸载它们。我错过了什么吗?
    • @Daniele:也许 MAF 是更好的前进方式:stackoverflow.com/questions/835182/…
    • 是的,但它使用多个 AppDomain,这使调用速度慢了三个数量级。我开始认为我要么放弃 C#,要么接受我必须每次重启我的应用程序时不时地。
    【解决方案2】:

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

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

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

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

    验证插件文件后,您可以随时在主进程中加载​​它,在任何域的探测路径(或在设置的动态路径中)创建卷影副本并以它而不是原始文件为目标,这有助于检查版本控制和更新。

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

    保持定时任务检查新插件和新版本,然后阻止插件模块的使用、交换文件、重新加载和取消阻止,如有必要,从以前的版本重新实例化新版本。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-09-01
      • 2013-06-26
      • 2019-06-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-29
      相关资源
      最近更新 更多