当前位置: 首页 > news >正文

.NET/C# 异常处理:写一个空的 try 块代码,而把重要代码写到 finally 中(Constrained Execution Regions)

不知你是否见过 try { } finally { } 代码中,try 块留空,而只往 finally 中写代码的情况呢?这种写法有其特殊的目的。

本文就来说说这种不一样的写法。


本文内容

      • 空的 try 块
      • 受约束的执行区域(Constrained Execution Regions)
        • 参考资料

空的 try 块

你可以点开这个链接查看 Exception 类,在里面你可以看到一段异常处理的代码非常奇怪:

// 代码已经过简化。
internal void RestoreExceptionDispatchInfo(ExceptionDispatchInfo exceptionDispatchInfo)
{
    // 省略代码。
    try{}
    finally
    {
        // 省略代码。
    }
    // 省略代码。
}

神奇之处就在于,其 try 块是空的,重要代码都放在 finally 中。那为什么会这么写呢?

在代码注释中的解释为:

We do this inside a finally clause to ensure ThreadAbort cannot be injected while we have taken the lock. This is to prevent unrelated exception restorations from getting blocked due to TAE.

翻译过来是:

finally 子句中执行此操作以确保在获取锁时无法注入 ThreadAbort。这是为了防止不相关的异常恢复因 TAE 而被阻止。

也就是说,此方法是为了与 Thread.Abort 对抗,防止 Thread.Abort 中断此处代码的执行。 Thread.Abort 的执行交给 CLR 管理,finally 的执行也是交给 CLR 管理。CLR 确保 finally 块执行的时候不会被 Thread.Abort 阻止。

代码在 .NET Core 和 .NET Framework 中的实现完全一样:

// This is invoked by ExceptionDispatchInfo.Throw to restore the exception stack trace, corresponding to the original throw of the
// exception, just before the exception is "rethrown".
[SecuritySafeCritical]
internal void RestoreExceptionDispatchInfo(System.Runtime.ExceptionServices.ExceptionDispatchInfo exceptionDispatchInfo)
{
    bool fCanProcessException = !(IsImmutableAgileException(this));
    // Restore only for non-preallocated exceptions
    if (fCanProcessException)
    {
        // Take a lock to ensure only one thread can restore the details
        // at a time against this exception object that could have
        // multiple ExceptionDispatchInfo instances associated with it.
        //
        // We do this inside a finally clause to ensure ThreadAbort cannot
        // be injected while we have taken the lock. This is to prevent
        // unrelated exception restorations from getting blocked due to TAE.
        try{}
        finally
        {
            // When restoring back the fields, we again create a copy and set reference to them
            // in the exception object. This will ensure that when this exception is thrown and these
            // fields are modified, then EDI's references remain intact.
            //
            // Since deep copying can throw on OOM, try to get the copies
            // outside the lock.
            object _stackTraceCopy = (exceptionDispatchInfo.BinaryStackTraceArray == null)?null:DeepCopyStackTrace(exceptionDispatchInfo.BinaryStackTraceArray);
            object _dynamicMethodsCopy = (exceptionDispatchInfo.DynamicMethodArray == null)?null:DeepCopyDynamicMethods(exceptionDispatchInfo.DynamicMethodArray);
            
            // Finally, restore the information. 
            //
            // Since EDI can be created at various points during exception dispatch (e.g. at various frames on the stack) for the same exception instance,
            // they can have different data to be restored. Thus, to ensure atomicity of restoration from each EDI, perform the restore under a lock.
            lock(Exception.s_EDILock)
            {
                _watsonBuckets = exceptionDispatchInfo.WatsonBuckets;
                _ipForWatsonBuckets = exceptionDispatchInfo.IPForWatsonBuckets;
                _remoteStackTraceString = exceptionDispatchInfo.RemoteStackTrace;
                SaveStackTracesFromDeepCopy(this, _stackTraceCopy, _dynamicMethodsCopy);
            }
            _stackTraceString = null;

            // Marks the TES state to indicate we have restored foreign exception
            // dispatch information.
            Exception.PrepareForForeignExceptionRaise();
        }
    }
}

你可以在 这里 查看 .NET Framework 版本,在这里 查看 .NET Core 的版本。

受约束的执行区域(Constrained Execution Regions)

这种现象在微软官方文档 可靠性最佳做法 中有介绍。

Doing so instructs the just-in-time compiler to prepare all the code in the finally block before running the try block. This guarantees that the code in the finally block is built and will run in all cases. It is not uncommon in a CER to have an empty try block. Using a CER protects against asynchronous thread aborts and out-of-memory exceptions. See ExecuteCodeWithGuaranteedCleanup for a form of a CER that additionally handles stack overflows for exceedingly deep code.

使用 try-finally 形成一个受约束的执行区域,使得 finally 中的代码被可靠地执行。


参考资料

  • 可靠性最佳做法 - Microsoft Docs
  • 受约束的执行区域 - Microsoft Docs
  • exception.cs - Reference Source
  • RestoreExceptionDispatchInfo
  • The empty try block mystery - Some Creativity
  • c# - Why use try {} finally {} with an empty try block? - Stack Overflow
  • corefx/System.Runtime.cs at master · dotnet/corefx

相关文章:

  • WindowsXamlHost:在 WPF 中使用 UWP 控件库中的控件
  • WindowsXamlHost:在 WPF 中使用 UWP 的控件(Windows Community Toolkit)
  • .NET/C# 在代码中测量代码执行耗时的建议(比较系统性能计数器和系统时间)
  • 编写 Target 检测 MSBuild / dotnet build 此次编译是否是差量编译
  • 使用 Win2D 绘制带图片纹理的圆(或椭圆)
  • Win2D 中的游戏循环:CanvasAnimatedControl
  • 使用 Windows 10 中的加速度计(Accelerometer,重力传感器)
  • 用 dotTrace 进行性能分析时,各种不同性能分析选项的含义和用途
  • 如何创建一个基于 .NET Core 3 的 WPF 项目
  • 将基于 .NET Framework 的 WPF 项目迁移到基于 .NET Core 3
  • 了解 .NET 的默认 TaskScheduler 和线程池(ThreadPool)设置,避免让 Task.Run 的性能急剧降低
  • .NET 中小心嵌套等待的 Task,它可能会耗尽你线程池的现有资源,出现类似死锁的情况
  • 在有 UI 线程参与的同步锁(如 AutoResetEvent)内部使用 await 可能导致死锁
  • 不要使用 Dispatcher.Invoke,因为它可能在你的延迟初始化 LazyT 中导致死锁
  • 定义一组抽象的 Awaiter 的实现接口,你下次写自己的 await 可等待对象时将更加方便
  • JavaScript-如何实现克隆(clone)函数
  • 【Under-the-hood-ReactJS-Part0】React源码解读
  • 3.7、@ResponseBody 和 @RestController
  • Computed property XXX was assigned to but it has no setter
  • ES6系列(二)变量的解构赋值
  • ES6语法详解(一)
  • Flannel解读
  • Java 11 发布计划来了,已确定 3个 新特性!!
  • JavaScript实现分页效果
  • java中的hashCode
  • LeetCode541. Reverse String II -- 按步长反转字符串
  • oldjun 检测网站的经验
  • react 代码优化(一) ——事件处理
  • vue 配置sass、scss全局变量
  • yii2权限控制rbac之rule详细讲解
  • 搞机器学习要哪些技能
  • 蓝海存储开关机注意事项总结
  • 批量截取pdf文件
  • 使用docker-compose进行多节点部署
  • 微服务框架lagom
  • 一个JAVA程序员成长之路分享
  • FaaS 的简单实践
  • raise 与 raise ... from 的区别
  • 交换综合实验一
  • 浅谈sql中的in与not in,exists与not exists的区别
  • (C语言)求出1,2,5三个数不同个数组合为100的组合个数
  • (Mirage系列之二)VMware Horizon Mirage的经典用户用例及真实案例分析
  • (pt可视化)利用torch的make_grid进行张量可视化
  • (Redis使用系列) SpirngBoot中关于Redis的值的各种方式的存储与取出 三
  • (多级缓存)多级缓存
  • (四)Linux Shell编程——输入输出重定向
  • (原創) 博客園正式支援VHDL語法著色功能 (SOC) (VHDL)
  • (转)c++ std::pair 与 std::make
  • (转)ObjectiveC 深浅拷贝学习
  • ***通过什么方式***网吧
  • .net framework profiles /.net framework 配置
  • .NET 使用 ILRepack 合并多个程序集(替代 ILMerge),避免引入额外的依赖
  • .NET 中什么样的类是可使用 await 异步等待的?
  • .net反编译工具
  • [ C++ ] template 模板进阶 (特化,分离编译)