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。如下所示
为了确保CER这种原理能够工作,首先CER之内不能在堆上进行任何分配操作,包括后台进行的分配操作。除了不能用New分配引用类型的对象之外,也不能进行装箱、线程同步锁操作或者访问多维数组。
刚才介绍到,CLR会事先编译CER中所有用到的方法以及它们各自调用的所有方法。那么聪明的人一定能看出一个问题,那就是通过委托和虚函数机制调用的方法无法事先准确判断,因而就无从准备。因此,RuntimeHelpers还提供了两个方法——PrepareMethod和PrepareDelegate。调用之前务必用这两个方法准备所有虚函数的实际版本和委托变量。使用CER是需要极其小心准备的,因此不是随随便便使用的特性。RuntimeHelpers还有许多其它方法对应各种有变数的情况。总之,CER的宗旨就是在执行之前将所有可以知道的情况尽数分析透彻以便提前判断CER中的操作到底有没有可能顺利完成。
下面用ThreadAbortException来做一个试验,因为这个异常是最容易引发的:
Imports System.Runtime.CompilerServices
Imports System.Threading

Module Module1

Dim globalArray() As Integer

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(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()
RuntimeHelpers.PrepareConstrainedRegions()
Try
Finally

For i As Integer = 0 To 50000000
globalArray(i) = 100
Next

End Try
End Sub

End Module
先把准备CER的代码注释掉,可以发现这个方法不是总能执行成功的,ThreadAbortException可能会将数组的操作打断,以至于留下不正常的状态。如果在你的计算机上该方法不会失败,可以尝试改变数组的大小。接下来应用CER,会发现出现异常时程序执行的速度剧烈下降,但是最终方法总能够成功地完成。这就是CER所带来的好处。
首先,需要提到异步异常的问题。异步异常就是指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()
Try
Catch
'注意,这里是CER
Finally
'这里也是CER
End Try
一般情况下都用Finally块来做CER。CER与普通代码不同,在CER执行期间CLR不能发出异步异常。因此CLR就必须采取一些措施。首先CER会将ThreadAbortException推迟到CER结束之后才发生,这比较容易做到。第二,为了避免OutOfMemoryException,CLR会将CER中用到的所有方法(注意,这里是从代码静态观察,而不是实际调用的方法)以及这些方法所调用到的所有方法全都编译成本地代码,然后根据情况预测可能的内存不足并提前到CER之前引发。然而,这个方法并不能对付堆栈益处错误,所以这个方法会事先保留48K的栈空间以防万一。然而根据MSDN文档,StackOverflowException还是可能会发生的。Try
Catch
'注意,这里是CER
Finally
'这里也是CER
End Try
为了确保CER这种原理能够工作,首先CER之内不能在堆上进行任何分配操作,包括后台进行的分配操作。除了不能用New分配引用类型的对象之外,也不能进行装箱、线程同步锁操作或者访问多维数组。
刚才介绍到,CLR会事先编译CER中所有用到的方法以及它们各自调用的所有方法。那么聪明的人一定能看出一个问题,那就是通过委托和虚函数机制调用的方法无法事先准确判断,因而就无从准备。因此,RuntimeHelpers还提供了两个方法——PrepareMethod和PrepareDelegate。调用之前务必用这两个方法准备所有虚函数的实际版本和委托变量。使用CER是需要极其小心准备的,因此不是随随便便使用的特性。RuntimeHelpers还有许多其它方法对应各种有变数的情况。总之,CER的宗旨就是在执行之前将所有可以知道的情况尽数分析透彻以便提前判断CER中的操作到底有没有可能顺利完成。
下面用ThreadAbortException来做一个试验,因为这个异常是最容易引发的:








































【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述