插件式框架探索.doc_第1页
插件式框架探索.doc_第2页
插件式框架探索.doc_第3页
插件式框架探索.doc_第4页
插件式框架探索.doc_第5页
已阅读5页,还剩10页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

【插件式框架探索系列】应用程序域(AppDomain) Posted on 2010-06-30 15:03 Kolor 阅读(1252) 评论(9) 编辑 收藏 所属分类: DotNet, WPF 应用程序域(AppDomain)已经不是一个新名词了,只要熟悉.net的都知道它的存在,不过我们还是先一起来重新认识下应用程序域吧,究竟它是何方神圣。应用程序域 众所周知,进程是代码执行和资源分配的最小单元,每个进程都拥有独立的内存单元,而进程之间又是相互隔离的,自然而然,进程成为了代码执行的安全边界。 一个进程对应一个应用程序是一个普遍的认知,而.net却打破了这一惯例,因为它带来了应用程序域这一全新的概念,CLR可使用应用程序域来提供应用程序之间的隔离,而一个进程中可以运行多个应用程序域,也就是说只要使用应用程序域,我们可以在一个进程中运行多个应用程序,而不会造成进程间调用或进程间切换等方面的额外开销。是不是觉得应用程序域是个很神奇的东东了,别急,我们再来看看它的隔离特性又为我们带来了什么。优势 首先,应用程序域之间是不相互影响的,它是天生的异常隔离机制。也就是说,在一个应用程序域中出现的错误不会影响到其他应用程序域,因为类型安全的代码不会导致内存错误。 其次,它能够在运行时动态的加载和卸载程序集。我们都知道,在.net世界中,加载器一旦加载了程序集,那么它将一直存在于应用程序的整个生命周期中,而应用程序域则改变了这一切,它为我们提供了卸载程序集的能力。 最后,应用程序域可以单独实施安全策略和配置策略。说白了就是可以为每个应用程序域配置相应的权限,以更好的管理应用程序。 另外值得注意的是,应用程序域和线程之间不具有一对一的相关性。在任意给定时间,在单个应用程序域中可以执行多个线程,而特定线程并不局限在单个应用程序域内。也就是说,线程可以自由跨越应用程序域边界,如果没有主动新启线程,那么多个应用程序域依然运行在同一个线程中。 总的来说,应用程序域形成了托管代码的隔离、卸载和安全边界。而这些特性带给一个插件式框架的将是异常隔离、动态加载卸载插件和更安全的插件运行环境。由于这篇文章的定位是针对框架设计结合应用程序域的特性,因此假设你已经对应用程序域有了一定的了解了,下面通过示例,让我们一步一步来认识应用程序域的这些特性。创建和卸载AppDomain 使用C#我们可以用如下的方式创建一个应用程序域,并在新域中执行一段代码: AppDomain domain = AppDomain.CreateDomain(Hello AppDomain!); domain.DoCallBack(new CrossAppDomainDelegate() = Window win = new Window Width = 300, Height = 100, Content = AppDomain.CurrentDomain.FriendlyName ; win.Show(); );运行后可以看到在新域中创建的Window展示如下:卸载应用程序域则可以通过AppDomain静态方法AppDomain.Unload(domain)实现,就是这么简单。配置域加载方式如果你运行了上面这段代码,是不是发现新域创建的Window过了好久才呈现出来,这是怎么回事呢,简单来说,这是因为.net加载器默认的行为是在每个域里都会重新加载引用的程序集(包括Framework本身除了mscorlib外的程序集),当然我们可以更改这种行为,不过在这之前我们先来了解下一个新概念”domain neutrality”, 详细资料可以看这篇文章Domain Neutral Assemblies,简单来说它拥有跨域共享程序集的能力,这就避免了重复加载的损耗,我们可以通过为程序入口main函数添加LoaderOptimization标签修改默认加载方式:System.STAThreadAttribute() System.Diagnostics.DebuggerNonUserCodeAttribute() LoaderOptimization( LoaderOptimization.MultiDomainHost) public static void Main() AppDomainTest.App app = new AppDomainTest.App(); app.InitializeComponent(); app.Run(); 重新编译运行,速度有了明显的提升吧。LoaderOptimization有三种方式(SingleDomain, MultiDomain和MultiDomainHost),在Domain Neutral Assemblies中均有详细的解译,有兴趣的朋友可以看下,此处就不再重述了。异常隔离 对于插件式框架而言,异常隔离是非常重要的,这是保证一个框架稳定性的必要特性。下面我们来看看使用应用程序域如何实现异常隔离。首先我们来模拟在新创建的域中抛出异常:AppDomain domain = AppDomain.CreateDomain(Hello AppDomain!); domain.DoCallBack(new CrossAppDomainDelegate() = Window win = new Window Width = 300, Height = 100, Content = AppDomain.CurrentDomain.FriendlyName ; win.Loaded += (obj, arg) = throw new Exception(test exception.); ; win.Show(); ); 这里采用的是在Window loaded事件中直接抛出异常达到模拟效果,OK,编译运行,很不幸,成功挂掉。 这是因为新域中未处理的异常,最终都会抛至默认域,进而导致崩溃。要验证这一点,很容易,我们只要在默认域中添加AppDomain.CurrentDomain.UnhandledException事件处理就可以截获到新域中抛出的异常,可惜在此你只能截获却无法改变崩溃的结果。那么如何才能处理掉这个异常,在默认域或者新域中注册System.Windows.Threading.Dispatcher.CurrentDispatcher.UnhandledException事件处理,示例如下:AppDomain domain = AppDomain.CreateDomain(Hello AppDomain!);System.Windows.Threading.Dispatcher.CurrentDispatcher.UnhandledException += (obj, arg) = arg.Handled = true; MessageBox.Show(arg.Exception.Message); AppDomain.Unload(domain);domain.DoCallBack(new CrossAppDomainDelegate() = Window win = new Window Width = 300, Height = 100, Content = AppDomain.CurrentDomain.FriendlyName ; win.Loaded += (obj, arg) = throw new Exception(test exception.); ; win.Show();); 注意到最关键的arg.Handled = true这一句,它的意义在于告诉系统这个事件已经被处理过了,不要再往下传递了,最后主动把新域卸载掉,而默认域则仍然正常运行着,如此便达到了异常隔离的效果。组合不同域中的插件 假设所有的插件都处于不同的域中,那么如何组合它们呢,即如何将不同域中的插件同时呈现到一个容器中。众所周知,要实现对象在域之间传递,对象必须是可序列化的或者是继承自MarshalByRefObject的类型,然而UI控件对此却是无能为力了, 在此就需要微软的Addin框架帮助了,虽然大家都觉得Addin框架复杂、难用,但是里面有好些东西还是很有用处的,比如这里将要用到的FrameworkElementAdapters类,它提供了两个静态方法ContractToViewAdapter和ViewToContractAdapter用于实现FrameworkElement和INativeHandleContract之间的相互转换,传说中这种转换是通过句柄实现的。还是用例子来说明如何让插件跨域呈现吧,首先添加System.Addin.Contract.dll和System.Windows.Presentation.dll两个引用,然后编写如下代码AppDomain domain = AppDomain.CreateDomain(test);domain.DoCallBack(new CrossAppDomainDelegate() = / 在新域中创建Button控件 Button btn = new Button Content = test ; / 将Button控件转换为INativeHandleContract INativeHandleContract ict = FrameworkElementAdapters.ViewToContractAdapter(btn); AppDomain.CurrentDomain.SetData(testbtn, ict););/ 在主域中获取新域中的INativeHandleContract对象INativeHandleContract iContract = domain.GetData(testbtn) as INativeHandleContract;/ 将INativeHandleContract对象转换回FrameworkElementFrameworkElement ctrl = FrameworkElementAdapters.ContractToViewAdapter(iContract);Application.Current.MainWindow.Content = ctrl;运行结果如下,新域中创建的控件成功的呈现在了主域中 至于域中的权限配置部分,将在下篇中讲述。谨以此记录成长的脚步,同时和大家一起分享快乐。【插件式框架探索系列】使用多UI线程提升性能 Posted on 2010-06-23 21:22 Kolor 阅读(1623) 评论(5) 编辑 收藏 所属分类: DotNet, WPF 了解WPF线程模型的都知道,UI线程负责呈现和管理UI,而UI元素(派生自DispatcherObject)只能由创建该元素的线程来访问,这就导致了一些耗时的UI操作将影响到整个应用程序性能,未响应及漫长的等待有时会令人抓狂,而UI线程一度成为了不可越逾的鸿沟。对于框架来说,一个插件的行为不应该影响到其它插件及整个平台的稳定性,后来在看了Running WPF Application with Multiple UI Threads和DispatcherObject与WPF线程模型两篇文章后,思维一下子就打开了,前一篇讲的是在WPF应用程序中使用多个UI线程,如果每个独立的插件都处于不同的UI线程,自然性能会有所提升,而后一篇则深入的分析了win32的消息循环和wpf的线程模型,非常透彻。下面是创建新UI线程的方法Thread thread = new Thread() = Window win = new Window Title = string.Format(Thread id:0, Thread.CurrentThread.ManagedThreadId) ; win.Show(); Dispatcher.Run(); ); thread.IsBackground = true; thread.SetApartmentState(ApartmentState.STA); thread.Start();如此Window将会在新的线程中运行,Dispatcher.Run()使当前线程进入消息循环,而设置线程的IsBackground为True是为了保证在程序退出时自动回收该线程,否则会出现进程驻留问题。谨以此记录成长的脚步,同时和大家一起分享快乐。【插件式框架探索系列】建立基于委托的订阅发布机制 Posted on 2010-05-10 00:10 Kolor 阅读(1508) 评论(7) 编辑 收藏 所属分类: DotNet 前些时候有个想法,想把自己感觉很有意思且方便平时开发的东西合起来,建立一个Framework,它会让开发变得更得心应手,可惜后来随着更多的东西被加入其中,越来越发觉这可能并不是一个开发者需要的东西,因为它太杂了,是的,作为一个Framework,还是单纯点的好,后来工作也忙了,就停滞了。这几天出差广州,夜来无事,便捡个模块来说道说道。Messenger在平时的开发中,数据的传递、处理等可以通过事件机制来完成,但是事件机制会引入耦合,当然这种耦合很多时候是合理的,但是却有那么一些时候这种耦合却不是你所需要的,过多的依赖会让你感到困惑,而引入Messenger就是为了解除这种依赖,请看下图。Figure 1. 类之间的单向依赖Figure 2. 类之间的双向依赖Figure 3. 引入Messenger后的依赖关系Messenger的概念来自MVVM Foundation项目,在看完该项目的源代码后,发现其中的Messenger是个很好的机制,就借鉴这个机制结合实际情况设计了自己的Messenger。顾名思义,Messenger在这里作为行为的传递者,其职责是预先接受行为的订阅,在需要的时候执行这些行为,即发布。Figure 4. Messenger类图从类图上我们看到Messenger提供了订阅、取消订阅和发布行为的能力。Messenger内置一个数据结构用于存储订阅的行为,存储结构描述为DictionaryT, List,T为行为在语义上的含义,作为字典的Key,其对应一组行为,而每个行为都以WeakAction表示,WeakAction会保证Messenger与定义行为的对象之间保持弱引用(WeakRefernce)关系,避免内存泄漏,如此便构成了行为的存储结构MessageToActionMap。Figure 5. MessageToActionMap类图Figure 6. WeakAction类图终于Messenger拥有了订阅和发布行为的能力,为了提供更好的灵活性,Messenger被定义为泛型抽象类型,具体的Messenger通过继承方式扩展,在此,我们定义了几种典型的Messenger实现:Firgure 7. Messenger实现 GeneralMessengerGeneralMessenger作为Messenger的一个通用实现,继承自Messenger,适用于大多数场景。 TypeMessengerTypeMessenger更适用于基于消息驱动的应用场景,在分布式应用中,系统间会互相传递消息,而消息的接收方往往会依据不同的消息类型作出不同的处理,比如Server收到Client的登录请求时会去执行验证行为,而收到登出请求时会作出释放资源行为。当然所有的这些,GeneralMessenger完全可以胜任,只不过TypeMessenger更加方便,后面提到的数据的订阅发布机制便是使用TypeMessenger实现的。 OnceOffMessengerOnceOffMessenger作为GeneralMessenger的特殊实现,它最特殊的地方便是订阅的行为,在发布后即消亡,适用于处理一次性的行为。为了适应各种不同的开发场景,Messenger本身是可以创建多个实例的,如果想实现单例模式,可以通过使用SingletonManager.GetInstance方法获取单例。Firgure 8. SingletonManager类图view source print?1TypeMessenger messenger = SingletonManager.GetInstance();数据的订阅发布机制发布者的职责是提供数据,而订阅者的职责则是消费数据,即处理数据,当然订阅者可以同时也是发布者,如此可以实现数据的再处理。为此定义了发布接口和订阅接口:Firgure 9. 订阅发布接口类图同时提供订阅和发布的标准实现:Firgure 10. 订阅发布标准实现类图我们可以看到订阅者同时也是发布者,两者皆是使用TypeMessenger实现订阅发布机制。SubscriberAdapter的Subscribe方法中订阅T的处理行为为Handle方法:view source print?1/ 2/ Subscribe the message handler. 3/ 4public void Subscribe() 5 6messenger.Subscribe(new Action(msg = Handle(msg); 7而在PublisherAdapter的OnMessage和OnMessageAsyn方法中发布数据:view source print?01/ 02/ Publish the message. 03/ 04/ The message. 05public virtual void OnMessage(object message) 06 07if (message != null) 08 09messenger.NotifyAll(message); 10 11 1213/ 14/ Publish the message asynchronously. 15/ 16/ The message. 17public virtual void OnMessageAsyn(object message) 18 19if (message != null) 20 21messenger.NotifyAllAsyn(message); 22 23 同时在SubscriberAdapter中也可以以类似的方式发布数据,该数据类型的订阅者会处理该数据。这种设计方式也是借鉴于Apache MINA框架中的过滤器概念,让数据在一个过滤器链中传递,链上的每个过滤器都有机会对数据进行处理和再加工。不过这里的订阅者的数据发布行为只能发布不同于当前类型的数据,否则会进入无限制的死循环,该行为已经通过重载PublisherAdapter的OnMessage和OnMessageAsyn方法实现。写在后面事件机制传递数据很方便,也比较简单,

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论