RSS 2.0 Feed
2006-06 Entries
摘要:CER是.NET 2.0 CLR方面的重要改进,旨在帮助那些对稳定性高度苛刻的程序对付.NET Framework的不稳定因素。因为普通的程序很少会用到,所以一直没有对这个特性加以足够重视。现在碰巧在翻译书籍的过程中用到,就一起来学习一下。首先,需要提到异步异常的问题。异步异常就是指OutOfMemoryException、StackOverflowException和ThreadAbortException等系统异常。说他们“异步”是因为他们可以在线程代码执行到任何地方的时候发生。一般的异常,比如FileNotFoundException是由代码自己产生的,因此可以用Try语句正常捕捉和处理。而异步异常则是CLR产生的。而且,这些异常都预示着非常严重的错误,代码自己通常都会手足无措。比方说内存耗尽了,代码自己即使Catch了也无济于事,都不知道刚刚哪一步出的问题,也不知道该怎么继续执行。ThreadAbortException通常是由Thread.Abort方法引发,如果要Abort的线程正在进行很关键的人物,比如修改一个全局对象的状态,那么发生ThreadAbortException可能会让整个程序的状态受损,进而产生错误的行为。因此,需要有种机制告诉CLR,我们要进行的事情很关键,不容打断,这就是CER——Constrained Execution Region。声明CER很简单,先调用System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions()方法,再紧接一个Try...Catch...Finally块即可。注意,这个Try必须紧接着PrepareConstrainedRegions()方法,而且,只有Catch和Finally块的内容成为CER。如下所示 RuntimeHelpers.PrepareConstrainedRegions()TryCatch    '注意,这里是CERFinally    '这里也是CEREnd Try一般情况下都用Finally块来做CER。CER与普通代码不同,在CER执行期间CLR不能发出异步异常。因此CLR就必须采取一些措施。首先CER会将ThreadAbortException推迟到CER结束之后才发生,这比较容易做到。第二,为了避免OutOfMemoryException,CLR会将CER中用到的所有方法(注意,这里是从代码静态观察,而不是实际调用的方法)以及这些方法所调用到的所有方法全都编译成本地代码,然后根据情况预测可能的内存不足并提前到CER之前引发。然而,这个方法并不能对付堆栈益处错误,所以这个方法会事先保留48K的栈空间以防万一。然而根据MSDN文档,StackOverflowException还是可能会发生的。为了确保CER这种原理能够工作,首先CER之内不能在堆上进行任何分配操作,包括后台进行的分配操作。除了不能用New分配引用类型的对象之外,也不能进行装箱、线程同步锁操作或者访问多维数组。刚才介绍到,CLR会事先编译CER中所有用到的方法以及它们各自调用的所有方法。那么聪明的人一定能看出一个问题,那就是通过委托和虚函数机制调用的方法无法事先准确判断,因而就无从准备。因此,RuntimeHelpers还提供了两个方法——PrepareMethod和PrepareDelegate。调用之前务必用这两个方法准备所有虚函数的实际版本和委托变量。使用CER是需要极其小心准备的,因此不是随随便便使用的特性。RuntimeHelpers还有许多其它方法对应各种有变数的情况。总之,CER的宗旨就是在执行之前将所有可以知道的情况尽数分析透彻以便提前判断CER中的操作到底有没有可能顺利完成。下面用ThreadAbortException来做一个试验,因为这个异常是最容易引发的: Imports System.Runtime.CompilerServicesImports System.ThreadingModule Module1Module Module1    Dim globalArray() As Integer    Sub Main()Sub Main()        globalArray = New Integer(50000000) {}        Dim t As New Thread(AddressOf Thread1)        t.Start()        t.Abort()        t.Join()        Console.WriteLine(AllEquals(globalArray, 100))    End Sub    Function AllEquals()Function AllEquals(Of T)(ByVal arr() As T, ByVal value As T) As Boolean        For i As Integer = 0 To arr.Length - 1            If Not arr(i).Equals(value) Then Return False        Next        Return True    End Function    Sub Thread1()Sub Thread1()        RuntimeHelpers.PrepareConstrainedRegions()        Try        Finally            For i As Integer = 0 To 50000000                globalArray(i) = 100            Next        End Try    End SubEnd Module 先把准备CER的代码注释掉,可以发现这个方法不是总能执行成功的,ThreadAbortException可能会将数组的操作打断,以至于留下不正常的状态。如果在你的计算机上该方法不会失败,可以尝试改变数组的大小。接下来应用CER,会发现出现异常时程序执行的速度剧烈下降,但是最终方法总能够成功地完成。这就是CER所带来的好处。...[阅读全文]

posted @ | Feedback (18) | Filed Under [ 技术随笔 ]

摘要:本文为一些泛型反射技巧的简单罗列,为日后的文章做准备。1、如何获得一个封闭构造类型(closed constructed type)的Type对象?假设有如下的类型: class TestType<T>class TestType<T, U>如果要获得封闭构造类型的Type对象,那么只需要用C#的typeof运算符,或者VB的GetType运算符作用于具体类型即可: //C#Type t1 = typeof(TestType<int>); 'VBDim t2 As Type = GetType(TestType(Of String))2、如何获取一个泛型类型(generic type)的Type对象?所谓泛型类型,就是有类型参数,但类型参数还未指定的原始定义。我们不能用TestType<T>这样的语法,因为T在我们的上下文中不存在。这时,可以用空的尖括号(C#)或空的Of语句(VB)来获取。 Type t1 = typeof(TestType<>);Type t2 = typeof(TestType<,>); Dim t1, t2 As Typet1 = GetType(TestType(Of ))t2 = GetType(TestType(Of ,))注意,我们可以用逗号来区别类型参数的个数。这就表明,泛型类型只能按类型参数的多少来重载,而不管有何种约束之类。这里获得的Type,就是类型参数未指定的泛型类型。3、如何从构造类型的Type对象生成泛型类型的Type对象?Type类的新增方法可以做到。 //C#Type ct = typeof(List<int>);//Get generic type definitionType gt = ct.GetGenericTypeDefinition();4、如何获取类型参数的Type对象?泛型类型的T, U等类型参数,以及运行中的实际取值,都是可以从Type对象获取的。 'VBDim t As Type = GetType(List(Of Integer))'Get the generic arguments, an arrayDim typeArgs As Type() = t.GetGenericArguments()'Get the first argument: Integer in this caseDim tArg0 As Type = typeArgs(0)5、从泛型类型Type对象生成构造类型的Type对象。通常可以用来从一种构造类型生成另一种构造类型 //C#Type ct = typeof(List<int>);Type gt = ct.GetGenericTypeDefinition();//Make another constructed type//The List<string> in this caseType ct2 = gt.MakeGenericType(typeof(string));6、如何取一个开放构造类型(open constructed type)的Type对象?开放构造类型是最难处理的一个,因为他们的类型参数已经指定,但没有指定为具体类型,而是指定为其他泛型类型的类型参数。这种类型在进行反射重载选取以及反射发出(Reflection Emit)操作的时候尤为重要。我们的手法就是,先从宿主泛型类型的定义中获取类型参数的类型,然后再建造出开放构造类型。这里,我们获得List<T>的构造函数的参数,IEnumerable<T>的类型,注意这里的T是List<T>所定义的,而不是泛型IEnumerable<T>自己的类型参数 'The generic type of List(Of T)Dim tlist As Type = GetType(List(Of ))'Get the "T" of List(Of T)Dim typeParam As Type = tlist.GetGenericArguments()(0)'the generic type of IEnumerable(Of T)Dim tienum As Type = GetType(IEnumerable(Of ))'make the open constructed typeDim tienumOpen As Type = tienum.MakeGenericType(typeParam)'只有用这种方法获得开放构造类型'你才能用这个语法获得真正想要的构造函数定义'因为构造函数定义里IEnumerable(Of T)是一个开放构造类型Dim c As ConstructorInfo = _    tlist.GetConstructor(New Type() {tienumOpen})大家可以回去结合试验理解这些用法。 ...[阅读全文]

posted @ | Feedback (8) | Filed Under [ 技术随笔 ]