教育网站制作定制,线上学编程哪个机构比较好,自己怎么设计证书模板,无极招聘信息网曾经也实现过.Net Framework 基于AppDomain 的 dll库热插拔#xff0c;经历了版本的迭代#xff0c;.Net Core 不支持 AppDomain#xff0c;之前也搞过.Net Core 3.1 版本的#xff0c;现在搞一下子.NET 6.0的。热插拔运用的场景主要运用到宿主与插件这个场景或者动态任务的… 曾经也实现过.Net Framework 基于AppDomain 的 dll库热插拔经历了版本的迭代.Net Core 不支持 AppDomain之前也搞过.Net Core 3.1 版本的现在搞一下子.NET 6.0的。热插拔运用的场景主要运用到宿主与插件这个场景或者动态任务的场景上假设你现在业务服务已经运行但是需要新增加新的业务功能就可以用这种方式。就像Office 或者 Visual Studio 一样它们都是集插件架构之大成者。逻辑实现主要是根据 AssemblyLoadContext 这个系统提供的API来实现的已经实现了对DLL程序集的加载和卸载。之前AppDomain是通过程序域隔离的环境的概念进行隔离的而 AssemblyLoadContext 的话提供了程序集加载隔离它允许在单个进程中加载同一程序集的多个版本。它替换.NET Framework中多个AppDomain实例提供的隔离机制其中AssemblyLoadContext.Default 表示运行时的默认上下文该上下文用于应用程序主程序集及其静态依赖项那么其他的上下文就是插件DLL的上下文了。从概念上讲加载上下文会创建一个用于加载、解析和可能卸载一组程序集的范围。这里就根据 AssemblyLoadContext 加载卸载来实现热插播逻辑的实现.实现逻辑主要的逻辑是这个逻辑/// summary/// dll文件的加载/// /summarypublic class LoadDll{/// summary/// 任务实体/// /summarypublic ITask _task;public Thread _thread;/// summary/// 核心程序集加载/// /summarypublic AssemblyLoadContext _AssemblyLoadContext { get; set; }/// summary/// 获取程序集/// /summarypublic Assembly _Assembly { get; set; }/// summary/// 文件地址/// /summarypublic string filepath string.Empty;/// summary/// 指定位置的插件库集合/// /summaryAssemblyDependencyResolver resolver { get; set; }public bool LoadFile(string filepath){this.filepath filepath;try{resolver new AssemblyDependencyResolver(filepath);_AssemblyLoadContext new AssemblyLoadContext(Guid.NewGuid().ToString(N), true);_AssemblyLoadContext.Resolving _AssemblyLoadContext_Resolving;using (var fs new FileStream(filepath, FileMode.Open, FileAccess.Read)){var _Assembly _AssemblyLoadContext.LoadFromStream(fs);var Modules _Assembly.Modules;foreach (var item in _Assembly.GetTypes()){if (item.GetInterface(ITask) ! null){_task (ITask)Activator.CreateInstance(item);break;}}return true;}}catch (Exception ex) { Console.WriteLine($LoadFile:{ex.Message}); };return false;}private Assembly _AssemblyLoadContext_Resolving(AssemblyLoadContext arg1, AssemblyName arg2){Console.WriteLine($加载{arg2.Name});var path resolver.ResolveAssemblyToPath(arg2);if (!string.IsNullOrEmpty(path)){using (var fs new FileStream(path, FileMode.Open, FileAccess.Read)){return _AssemblyLoadContext.LoadFromStream(fs);}}return null;}public bool StartTask(){bool RunState false;try{if (_task ! null){_thread new Thread(new ThreadStart(_Run));_thread.IsBackground true;_thread.Start();RunState true;}}catch (Exception ex) { Console.WriteLine($StartTask:{ex.Message}); };return RunState;}private void _Run(){try{_task.Run();}catch (Exception ex) { Console.WriteLine($_Run 任务中断执行:{ex.Message}); };}public bool UnLoad(){try{_thread?.Interrupt();}catch (Exception ex){ Console.WriteLine($UnLoad:{ex.Message});}finally{_thread null;}_task null;try{_AssemblyLoadContext?.Unload();}catch (Exception){ }finally{_AssemblyLoadContext null;GC.Collect();GC.WaitForPendingFinalizers();}return true;}}以上就是这个热插拔的核心逻辑了。ITask.cs这个接口实现简单只有一个方法当然如果有需要可以扩展一下。/// summary/// 任务接口/// /summarypublic interface ITask{/// summary/// 任务的运行方法/// /summary/// returns/returnsvoid Run();}插件库1 PrintStrLib插件的代码就很简单了public class PrintStr : ITask{public void Run(){int a 0;while (true){Console.WriteLine($PrintStr:{a});a;Thread.Sleep(1 * 1000);}}}插件库2 PrintDateLib插件的代码就很简单了public class PrintDate : ITask{public void Run(){while (true){Console.WriteLine($PrintDate:{DateTime.Now});Thread.Sleep(1 * 1000);}}}测试运行使用也很简单加载程序集然后执行间隔指定时间后顺序卸载。static void Main(string[] args)
{Console.Title AssemblyLoadContext Dll热插拔 测试 by 蓝总创精英团队;var list new ListLoadDll();Console.WriteLine(开始加载DLL);list.Add(Load(Path.Combine(AppDomain.CurrentDomain.BaseDirectory,DLL, PrintDateLib.dll)));list.Add(Load(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, DLL, PrintStrLib.dll)));foreach (var item in list){item.StartTask();}Console.WriteLine(开启了任务!);SpinWait.SpinUntil(() false, 5 * 1000);foreach (var item in list){var s item.UnLoad();SpinWait.SpinUntil(() false, 2 * 1000);Console.WriteLine($任务卸载{s});}Console.WriteLine(热插拔插件任务 测试完毕);Console.ReadLine();
}
public static LoadDll Load(string filePath)
{var load new LoadDll();load.LoadFile(filePath);return load;
}效果查看从下图来看我们想要的结果都有了加载两个插件插件执行自己的业务然后顺序一个一个的卸载掉确实已经不在执行它自己的业务了。总结实际上.Net的程序集的隔离问题很多这种隔离方式实际用的过程中如果程序集简单还好复杂的话可能会有别的问题。我非常喜欢的隔离方式就像谷歌游览器那样的插件方式或者IIS那样的容器级隔离不过这种实际上我分析是进程级隔离方案现在也流行docker系统级隔离。只能说存在即合理吧有它存在的价值。代码地址https://github.com/kesshei/AssemblyLoadContextDemo.githttps://gitee.com/kesshei/AssemblyLoadContextDemo.git参考文档https://docs.microsoft.com/zh-cn/dotnet/api/system.runtime.loader.assemblyloadcontext?viewnet-6.0阅一键三连呦感谢大佬的支持您的支持就是我的动力!