博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
应用程序域 z
阅读量:5837 次
发布时间:2019-06-18

本文共 4253 字,大约阅读时间需要 14 分钟。

应用程序域(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”, 详细资料可以看这篇文章,简单来说它拥有跨域共享程序集的能力,这就避免了重复加载的损耗,我们可以通过为程序入口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, MultiDomainMultiDomainHost),在中均有详细的解译,有兴趣的朋友可以看下,此处就不再重述了。

异常隔离 

    对于插件式框架而言,异常隔离是非常重要的,这是保证一个框架稳定性的必要特性。下面我们来看看使用应用程序域如何实现异常隔离。

首先我们来模拟在新创建的域中抛出异常:

 

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类,它提供了两个静态方法和用于实现和之间的相互转换,传说中这种转换是通过句柄实现的。还是用例子来说明如何让插件跨域呈现吧,首先添加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对象转换回FrameworkElement
FrameworkElement ctrl
=
FrameworkElementAdapters.ContractToViewAdapter(iContract);
Application.Current.MainWindow.Content
=
ctrl;

 

 

运行结果如下,新域中创建的控件成功的呈现在了主域中

 

 

转载地址:http://bhccx.baihongyu.com/

你可能感兴趣的文章
redis 内存管理分析
查看>>
Sharding-JDBC 最大努力型事务理解
查看>>
扩展segment数量
查看>>
Cisco 交换机端口安全
查看>>
cv.Mat 与 .txt 格式文件读写操作
查看>>
JavaScript事件练习
查看>>
vs2010的11个调试技巧和方法
查看>>
关于k-means聚类算法的matlab实现
查看>>
Git分支2
查看>>
在域环境下禁用USB存储设备的方法
查看>>
一键安装Gitlab后的备份、迁移与恢复
查看>>
因为本人工作繁忙,精力有限,本博客停止更新。有兴趣的博友可以关注我在CSDN上的主博客...
查看>>
Node实现静态服务器
查看>>
lsa声卡/dev/snd/pcmC0D0p的open打开流程
查看>>
浅谈什么是正向代理和反向代理,如何使用nginx搭建正向代理和反向代理
查看>>
转 通过phpize为php在不重新编译php情况下安装模块openssl
查看>>
html基础
查看>>
深入理解java虚拟机(三)--类文件结构
查看>>
Null value was assigned to a property of primitive type setter of
查看>>
UIRecorder 学习了解
查看>>