【问题标题】:Code demonstrating the importance of a Constrained Execution Region演示受限执行区域重要性的代码
【发布时间】:2010-11-09 05:24:18
【问题描述】:

除非应用[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)],否则任何人都可以创建一个简短的示例吗?

我刚刚浏览了这个sample on MSDN,即使我注释掉了 ReliabilityContract 属性,我也无法让它崩溃。最后似乎总是被调用。

【问题讨论】:

    标签: c# .net concurrency cer


    【解决方案1】:

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

    查看此页面,了解 Success 和 MayFail 状态之间的差异,因为它与 Array.CopyTo 方法有关:http://weblogs.asp.net/justin_rogers/archive/2004/10/05/238275.aspx

    【讨论】:

    • CER 主要用于保护托管 CLR 在 appdomain 拆卸和其他异常情况期间免受损坏状态。 SQL Server 等主机可能会触发粗鲁中止,在粗鲁中止期间,finally 子句可能会被中断。
    • 更明确地说,CER 允许开发人员编写代码以保护自己在异常情况下不破坏自己的状态。保护 CLR 的 CER 没有什么固有的,它只是使编写可以保护自身的代码成为可能。肮脏的秘密在 CLR 1.0/1.1 中,编写可以成功处理某些类异常的代码是不可能的。
    • 链接已损坏。
    【解决方案2】:

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

    它们大多是“保留以备将来使用”。

    【讨论】:

    • 尽管投了反对票,但缺乏这个问题的例子证明了我的观点。 CER 不影响代码流,包括 finally 块。他们记录代码,以便框架可以区别对待。基本上,它转化为“我不会引发异步异常,你不会用异步异常打断我”。删除 CER 永远不会破坏代码,尽管在统计上它可能会降低其可靠性。很明显,CER 的用途远不止目前由运行时实现的。
    • CER 属性不仅用于文档;当在受约束的执行区域内使用时,将在 JIT 执行之前准备好标有 ReliabilityContract 的方法以进行预编译,并且在进入 PrepareConstrainedRegions 时为该方法预分配内存。
    • 它们是文档。正如我也说过的,CLR 会阅读和使用该文档 - 这里没有矛盾。关键是,“除非应用 [],否则中断的短样本”是不可能的,没有 CER 属性就不会中断。变得不那么可靠 - 是的,但没有什么可以被描述为“代码中断”。
    【解决方案3】:

    您是否在调试器下运行 MSDN 示例?我认为当您在调试器中执行时,CER 不可能起作用,因为无论如何调试器本身都会改变执行的性质。

    如果您在优化发布模式下构建和运行应用程序,您应该能够看到它失败。

    【讨论】:

      【解决方案4】:

      此功能的主要驱动力是支持 SQL Server 将 CLR 集成到 SQL Server 2005 中的严格要求。可能是为了让其他人可以使用并且可能出于法律原因,这种深度集成已作为托管 API 发布,但技术要求是SQL 服务器。请记住,在 SQL Server 中,MTBF 以月而不是小时为单位衡量,并且由于发生未处理的异常而重新启动进程是完全不可接受的。

      这个MSDN Magazine article 可能是我见过的描述受约束的执行环境所针对的技术要求的最好的一个。

      ReliabilityContract 用于装饰您的方法,以指示它们如何根据潜在的异步异常(ThreadAbortException、OutOfMemoryException、StackOverflowException)进行操作。受约束的执行区域定义为 try 块的 catch 或 finally(或故障)部分,紧接在调用 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 获得最大收益的环境。

      【讨论】:

      • 这并不完全正确。 CER 也用于SafeHandles 以确保它们已关闭。这包括导致 OS SafeHandle(GDI+、文件等)的任何 P/Invoke。如果您开始在 Windows 中泄漏句柄,您很快就会遇到麻烦。
      【解决方案5】:
      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 被jitted 时,它会尝试从finally 块中创建一个ConstrainedRegion

      • 如果没有ReliabilityContract,,则无法形成正确的ConstrainedRegion,因此会发出常规代码。调用Stackoverflow(在try块执行后)抛出堆栈溢出异常。

      • ReliabilityContract 的情况下,可以形成ConstrainedRegion,并且可以将finally 块中的方法的堆栈要求提升到MyFn。现在调用 MyFn 时会引发堆栈溢出异常(在执行 try 块之前)。

      【讨论】:

      • 请注意,这段代码似乎在 StackOverflow 异常上正常失败,但实际上并非如此。如果您通过删除大行并递归调用 StackOverflow 来破坏堆栈,则不会触发 OOM 并且不会捕获 SO 异常。这是PrepareConstrainedRegions (afaik) 的限制。另请参阅 Richter 的 CLR 书籍:books.google.nl/…
      猜你喜欢
      • 2023-04-04
      • 1970-01-01
      • 1970-01-01
      • 2011-05-29
      • 2013-03-19
      • 2015-05-30
      • 1970-01-01
      • 2017-09-27
      • 1970-01-01
      相关资源
      最近更新 更多