展示受限执行区域重要性的代码

40

有人能否创建一个简短的示例,除非应用了[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]否则会出错?

我刚刚在MSDN上运行了这个示例,即使我注释掉了ReliabilityContract属性,也无法让它崩溃。最终似乎总是被调用。

5个回答

51
using System;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;

class Program {
    static bool cerWorked;

    static void Main( string[] args ) {
        try {
            cerWorked = true;
            MyFn();
        }
        catch( OutOfMemoryException ) {
            Console.WriteLine( cerWorked );
        }
        Console.ReadLine();
    }

    unsafe struct Big {
        public fixed byte Bytes[int.MaxValue];
    }

    //results depends on the existance of this attribute
    [ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success )] 
    unsafe static void StackOverflow() {
        Big big;
        big.Bytes[ int.MaxValue - 1 ] = 1;
    }

    static void MyFn() {
        RuntimeHelpers.PrepareConstrainedRegions();
        try {
            cerWorked = false;
        }
        finally {
            StackOverflow();
        }
    }
}

MyFn 被 jit 编译时,它会尝试从 finally 块创建一个 ConstrainedRegion

  • 在没有 ReliabilityContract 的情况下,无法形成适当的 ConstrainedRegion,因此会生成常规代码。栈溢出异常会在调用 Stackoverflow(try 块执行后)时抛出。

  • 如果有 ReliabilityContract,则可以形成一个 ConstrainedRegion,并且 finally 块中方法的堆栈要求可以提升到 MyFn 中。现在,栈溢出异常会在调用 MyFn 时抛出(在 try 块执行之前)。


3
请注意,这段代码似乎能够优雅地处理StackOverflow异常,但实际上并不是这样。如果通过删除大块内容破坏堆栈,并递归调用StackOverflow,将不会触发OOM,并且未捕获SO异常。这是“PrepareConstrainedRegions”的一个限制(据我所知)。请参阅Richter的CLR书籍:http://books.google.nl/books?id=QMdCf_mm55cC&pg=PT1016&lpg=PT1016&dq=ExecuteCodeWithGuaranteedCleanup+stackoverflowexception&source=bl&ots=V942WGDtFy&sig=I8shxrKexuEKnJPvQf7Cqzck6SE&hl=en&sa=X&ei=NYnpUI-BI8-S0QX6q4GgAw&redir_esc=y - Abel

22
这个功能的主要驱动因素是支持 SQL Server 2005 将 CLR 集成到 SQL Server 中的严格要求。可能为了让其他人可以使用,也可能出于法律原因,这种深度集成被发布为托管 API,但技术要求是 SQL Server 的。请记住,在 SQL Server 中,MTBF 是以月计算而不是小时计算的,进程由于发生未处理的异常而重新启动是完全不可接受的。 MSDN Magazine article 这篇文章可能是我见过的最好的描述约束执行环境构建所需技术要求的文章。
ReliabilityContract 用于装饰您的方法,以指示它们在可能的异步异常(ThreadAbortException、OutOfMemoryException、StackOverflowException)方面的操作方式。约束执行区域被定义为 try 块的 catch 或 finally(或 fault)部分,其紧接着一个对 System.Runtime.CompilerServices.RuntimeServices.PrepareConstrainedRegions() 的调用。
System.Runtime.CompilerServices.RuntimeServices.PrepareConstrainedRegions();
try 
{
    // this is not constrained
} 
catch (Exception e) 
{
    // this IS a CER
} 
finally 
{
    // this IS ALSO a CER
}

当在CER中使用ReliabilityContract方法时,会发生两件事情。该方法将被JIT预先准备,以便在第一次执行时不会调用JIT编译器,因为它可能会尝试使用内存并引发自己的异常。同时,在CER内部,运行时承诺不会抛出ThreadAbort异常,并将等待直到CER完成后再抛出异常。
所以回到你的问题;我仍在努力想出一个简单的代码示例,直接回答你的问题。正如你可能已经猜到的那样,由于问题的异步性质,最简单的示例将需要相当多的代码,并且可能是SQLCLR代码,因为这是使用CER获得最大利益的环境。

3
这并不完全正确。CER也用于SafeHandles以确保它们被关闭。这包括任何导致操作系统SafeHandle(GDI+、文件等)的P/Invoke。如果在Windows中泄漏句柄,很快就会遇到麻烦。 - Jeremy

3

您是否在调试器下运行MSDN示例?我认为当您在调试器中执行时,CER无法工作,因为调试器本身会改变执行方式。

如果您以优化的发布模式构建并运行应用程序,则应该能够看到它失败。


0

虽然我没有具体的例子给你,但我认为你忽略了在方法内部使用try..finally块来保证成功的重点。声明该方法总是成功的意思是无论执行过程中发生什么(异常),都会采取措施确保在方法返回时所访问的数据处于有效状态。如果没有try..finally,你将无法确保任何事情,并且可能意味着你想要发生的操作只有一半会发生。因此,Cer.Success并不实际上保证成功,它只表示作为开发人员的你保证成功。

查看这个页面,了解Success和MayFail状态之间差异的解释,因为它涉及到一个Array.CopyTo方法:http://weblogs.asp.net/justin_rogers/archive/2004/10/05/238275.aspx


1
CER主要用于保护托管的CLR在应用程序域拆除和其他异常情况期间不受损坏。像SQL Server这样的宿主可能会触发粗鲁的中止,在粗鲁的中止期间,finally子句可能会被中断。 - Sam Saffron
4
稍微明确一些,CER是为了让开发者具备编写代码的能力,以保护其自身在异常情况下的状态而存在。 CER并没有本质上的保护CLR的作用,它只是让编写能够自我保护的代码成为可能。 在CLR 1.0/1.1中,dirty secret是无法编写能够成功处理某些类别异常的代码。 - Peter Oehlert
2
链接已损坏。 - doug65536

-5

CER 属性是文档的一种方式。它们确实会影响 CLR 在某些情况下执行代码的方式,但我相信它们(或缺乏它们)在当前版本的 .NET 中永远不会导致错误。

它们大多数是“为将来保留”的。


尽管有负面评价,但是这个问题缺乏示例证明了我的观点。CER不会影响代码流程,包括finally块。它们记录代码,以便框架可以对其进行不同的处理。基本上,它的意思是“我不会引发异步异常,你也不要用异步异常打扰我”。移除CER不会破坏代码,尽管这可能使它在统计上变得不太可靠。很明显,CER可以用于比运行时当前实现的更多方面。 - ima
6
CER属性不仅用于文档记录;当在受限执行区域内使用时,带有ReliabilityContract标记的方法将在执行之前被JIT预编译,并且在进入PrepareConstrainedRegions时为方法预分配内存。 - Peter Oehlert
它们是文档。正如我所说,CLR会阅读和使用这些文档 - 这里没有矛盾。关键是,“除非应用[],否则会出现短样本中断”的情况是不可能的,没有CER属性就不会出现任何问题。变得不太可靠 - 是的,但没有什么可以描述为“代码中断”。 - ima

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接