简体   繁体   English

.NET多代理程序仿真(运行时加载/卸载)的插件体系结构

[英]Plugin architecture for .NET multi-agent simulation (runtime load/unload)

DESCRIPTION 描述

I am currently designing an architecture for a C# multiagent simulation, where agent actions are driven by many modules in their "brain", which may read sensors, vote for an action or send messages/queries to other modules (all of this is implemented through the exchange of messages). 我目前正在设计一种用于C#多主体仿真的体系结构,其中,代理动作由其“大脑”中的许多模块驱动,这些模块可以读取传感器,对动作进行投票或将消息/查询发送到其他模块(所有这些都是通过以下方式实现的信息交换)。 Of course, modules can have a state. 当然,模块可以具有状态。

Modules run in parallel : they have an update method which consumes messages and queries, and perform some sort of computation. 模块并行运行:它们具有一个update方法,该方法使用消息和查询,并执行某种计算。 The update methods return iterators, and have multiple yields in their bodies, so that I can schedule modules cooperatively. 更新方法返回迭代器,并且在其主体中具有多个收益,因此我可以协作地调度模块。 I do not use a single thread for each module because I expect to have hundreds to thousands of modules for every agent, which would lead to a huge amount of RAM occupied by thread overhead. 我不为每个模块使用单个线程,因为我希望每个代理都有数百至数千个模块,这将导致线程开销占用大量RAM。

I would like these modules to behave like runtime plugins , so that while the simulation is running I can add new module classes and rewrite/debug existing ones, without ever stopping the simulation process, and then use those classes to add and remove modules from the agents' brains, or just let existing modules change their behaviours due to new implementations of their methods. 我希望这些模块的行为类似于运行时插件 ,以便在模拟运行时,我可以添加新的模块类并重写/调试现有的类,而无需停止模拟过程,然后使用这些类在模块中添加和删除模块。代理商的大脑,或者只是让现有模块由于其方法的新实现而改变其行为。

POSSIBLE SOLUTIONS 可能的解决方案

I have come up with a number of possible solutions in the last few days, which all have something disappointing: 最近几天,我提出了许多可能的解决方案,但都令人失望:

  1. Compile my modules into DLLs, load each in a different AppDomain and then use AppDomain.CreateInstanceFromAndUnwrap() to instantiate the module, which I would then cast to some IModule interface, shared between my simulation and the modules (and implemented by each module class). 将我的模块编译成DLL,将每个模块加载到不同的AppDomain中 ,然后使用AppDomain.CreateInstanceFromAndUnwrap()实例化该模块,然后将其转换为一些IModule接口,该接口在我的仿真和模块之间共享(并由每个模块类实现) 。 The interface would expose just the SendMessage, the Update and a few other members, common to all modules. 该接口将仅公开所有模块共有的SendMessage,Update和其他一些成员。

    • The problem with this solution is that calls between AppDomains are much slower than direct calls (within the same AppDomain). 此解决方案的问题在于AppDomain之间的调用比直接调用(在同一AppDomain中)要慢得多。
    • Also, I don't know the overhead of AppDomains, but I suppose that they are not free, so having thousands could become a problem. 另外,我不知道AppDomains的开销,但我想它们不是免费的,因此拥有数千个可能成为问题。
  2. Use some scripting language for the modules, while keeping C# for the underlying engine, so that there is no assembly loading/unloading. 对模块使用某种脚本语言 ,同时为基础引擎保留C#,这样就不会加载/卸载程序集。 Instead, I would host an execution context for the scripting language for each module. 相反,我将为每个模块的脚本语言托管一个执行上下文。

    • My main concern is that I do not know a scripting language which is big (as in 'python, lua, ruby, js are big, Autoit and Euphoria are not') fast , embeddable into .NET and allows step by step execution (which I need in order to perform cooperative scheduling of module execution). 我主要担心的是,我不知道大型的脚本语言(例如,在“ python,lua,ruby,js大,Autoit和Euphoria则不是”)中是快速的可嵌入到.NET中并允许逐步执行 (我需要执行模块执行的协作调度)。
    • Another concern about this is that I suppose I'd have to use a runtime context for each module, which in turn would have massive overhead. 与此相关的另一个问题是,我想我必须为每个模块使用运行时上下文,这反过来又会产生大量开销。
    • Lastly, I suppose a scripting language would be probably slower than C#, which would reduce performance. 最后,我认为脚本语言可能会比C#慢,从而降低性能。
  3. Avoid unloading of assemblies, instead renaming/versioning them somehow, so that I can have a ton of different versions, then just use the latest one for each type. 避免卸载程序集,而是以某种方式重命名/版本化它们,以便我可以拥有大量不同的版本,然后为每种类型使用最新的版本。

    • I'm not even sure this is possible (due to omonimous types and namespaces) 我什至不确定这是可能的(由于类型和名称空间的限制)
    • Even if possible, it would be very memory-inefficient. 即使可能,这也将是非常低效的内存。
  4. Do a transparent restart of the simulation, which means pausing the simulation (and execution of the scheduler of brains/modules), serializing everything (including every module), exiting the simulation, recompiling the code, starting the simulation again, deserializing everything, catching any exception raised due to the changes I made to the class and resuming execution. 透明地重新启动仿真,这意味着暂停仿真(以及执行大脑/模块的调度程序),序列化所有内容(包括每个模块),退出仿真,重新编译代码,再次启动仿真,反序列化所有内容,捕获由于我对类所做的更改并恢复执行而引发的任何异常。

    • This is a lot of work, so I consider it my last resort. 这是很多工作,所以我认为这是我的最后选择。
    • Also, this whole process would be very slow at some point, depending on number of modules and their sizes, making it impractical 同样,整个过程有时会非常缓慢,具体取决于模块的数量及其大小,这使其不切实际

I could overcome this last problem (the whole process in solution 4 becoming slow), by mixing solutions 3 and 4, loading many many assemblies with some form of versioning and performing a restart to clean up the mess every now and then. 通过混合解决方案3和4,我可以克服最后一个问题(解决方案4中的整个过程变慢),以某种形式的版本化加载许多程序集,然后不时地重新启动以清理混乱。 Yet, I would prefer something that doesn't interrupt the whole simulation just because I made a small change in a module class. 但是,我宁愿不要因为我在模块类上做了些小改动而不会打断整个模拟的东西。

ACTUAL QUESTION 实际问题

So here is my question(s): is there any other solution? 所以这是我的问题:还有其他解决方案吗? Did I miss any workaround to the problems of those I found? 我是否错过任何针对所发现问题的解决方法? For example, is there some scripting language for .NET which satisfies my needs (solution #2)? 例如,是否有一些.NET脚本语言可以满足我的需求(解决方案2)? Is versioning possible, in the way I vaguely described it(Solution #3)? 以我隐约描述的方式(解决方案3)可以进行版本控制吗?

Or even, more simply: is .NET the wrong platform for this project? 甚至更简单地说:.NET是该项目的错误平台吗? (I'd like to stick with it because C# is my main language, but I could see myself doing this in Python or something alike if necessary) (我想坚持使用它,因为C#是我的主要语言,但是我可以看到自己使用Python或其他类似方法进行此操作)

您是否考虑过托管可扩展性框架

I'm working in a simulation system that works in a very similar way, treating agent modules as plugins. 我正在一个模拟系统中工作,该系统的工作方式非常相似,将代理模块视为插件。

I created a Plugin Manager that handles every Domain loading related things, checking plugin validity in a dummy domain and then hotloading it in the engine domain. 我创建了一个插件管理器来处理与每个域加载相关的事情,在虚拟域中检查插件的有效性,然后在引擎域中热加载它。

Using AppDomain is where you can get the full control, and you can reduce process time by running your Plugin Manager's tasks in parallel. 使用AppDomain是可以完全控制的地方,并且可以通过并行运行插件管理器的任务来减少处理时间。

AppDomains aren't cost free, but you can handle it using only two (or three if you need more isolation between validation and execution domains). AppDomain不是免费的,但是您可以仅使用两个(如果需要在验证和执行域之间进行更多隔离,则​​可以使用三个)来处理它。

Once a plugin file is validated you can load it in the very main process at any time, creating a shadow copy in any domain's probing path (or in dynamic path if set) and targeting it instead of original file is useful to check versioning and updates. 插件文件通过验证后,您可以随时在最主要的过程中加载它,在任何域的探测路径(或动态路径,如果设置)中创建卷影副本,并以它而不是原始文件为目标对检查版本和更新很有用。 。

Using a domain for validation and another to execution may require a swap context, who takes care of previous version instances while updating. 使用域进行验证并执行另一个域可能需要交换上下文,该上下文在更新时会照顾以前的版本实例。

Keeping a time scheduled task to check new plugins and new versions, and then block plugin module usage, swap files, reload, and unblock, reinstancing new versions from previous if necessary. 保持一个计划好的任务,以检查新插件和新版本,然后阻止插件模块使用,交换文件,重新加载和取消阻止,并在必要时从以前的新版本重新启动。

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

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