代码之家  ›  专栏  ›  技术社区  ›  Dzinx

C使用语句abort安全吗?

  •  25
  • Dzinx  · 技术社区  · 14 年前

    using 陈述。根据这本书(第138页),

    using (StreamReader reader = File.OpenText("file.txt")) {
        ...
    }
    

    完全等同于:

    StreamReader reader = File.OpenText("file.txt");
    try {
        ...
    } finally {
        if (reader != null)
            ((IDisposable)reader).Dispose();
    }
    

    thread.Abort() ,所以 ThreadAbortException 被抛出,并假设线程正好在初始化读取器之后并且在输入 try..finally

    一个可能的解决方案是这样编码:

    StreamReader reader = null;
    try {
        reader = File.OpenText("file.txt");
        ...
    } finally {
        if (reader != null)
            ((IDisposable)reader).Dispose();
    }
    

    这是安全的。

    现在我要问:

    1. 这本书的作者是对的吗 使用 语句不是中止安全的,或者它们是错误的,它的行为类似于我的第二个解决方案?
    2. 如果 使用 相当于第一个变量(非中止安全),为什么要检查 null 在里面 finally ?
    3. 根据这本书(第856页), 线程异常

    编辑: 我知道使用 线程。中止() 不被认为是好的做法。我的兴趣纯粹是理论上的:如何 言行举止 确切地 ?

    9 回复  |  直到 14 年前
        1
  •  17
  •   Joe Albahari    14 年前

    这本书的配套网站有更多关于中止线程的信息 here .

    第二个问题的答案是,在某些情况下,变量可以合法地为空。例如,GetFoo()可能在这里返回null,在这里,您不希望在隐式finally块中引发NullReferenceException:

    using (var x = GetFoo())
    {
       ...
    }
    

    要回答您的第三个问题,使中止安全的唯一方法(如果您正在调用框架代码)是在之后删除AppDomain。在许多情况下,这实际上是一个实用的解决方案(这正是LINQPad在取消正在运行的查询时所做的)。

        2
  •  8
  •   Clyde    14 年前

    两个场景之间实际上没有区别——在第二个场景中,ThreadAbort仍然可能发生在调用OpenText之后,但在将结果分配给读取器之前。

    基本上,当您得到ThreadAbortException时,所有的赌注都会被取消。这就是为什么您不应该故意中止线程,而应该使用其他方法优雅地结束线程。

    针对您的编辑——我再次指出,您的两个场景实际上是相同的。除非File.OpenText调用 完成并返回一个值,因此第一种方式与第二种方式写出代码没有区别。

        3
  •  6
  •   Marc Gravell    14 年前

    Thread.Abort 是非常糟糕的jujuju;如果有人打电话说你已经在 很多 有麻烦(无法恢复的锁等)。 线程.中止 真的应该限制在吸入一种疾病的过程中。

    通常地 展开干净,但在极端情况下无法保证每一位代码都能执行。一个更紧迫的例子是“如果电源故障会发生什么?”。

    回复 null File.OpenText 返回 无效的 ? 好吧,它 不会 但是 编译程序 不知道。

        4
  •  4
  •   CodesInChaos    14 年前

    有点离题,但是在线程中止期间lock语句的行为也很有趣。而lock相当于:

    object obj = x;
    System.Threading.Monitor.Enter(obj);
    try {
        …
    }
    finally {
        System.Threading.Monitor.Exit(obj);
    }
    

    保证(由x86抖动)在Monitor.Enter和try语句之间不会发生线程中止。
    http://blogs.msdn.com/b/ericlippert/archive/2007/08/17/subtleties-of-c-il-codegen.aspx

    生成的IL代码在.net 4中似乎不同:
    http://blogs.msdn.com/b/ericlippert/archive/2009/03/06/locks-and-exceptions-do-not-mix.aspx

        5
  •  2
  •   CodesInChaos    14 年前

    语言规范明确指出第一个是正确的。

    http://msdn.microsoft.com/en-us/vcsharp/aa336809.aspx MS规范(Word文档)
    http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-334.pdf ECMA规范

    在线程中止的情况下,两种代码变体都可能失败。如果中止发生在计算表达式之后,但在分配给局部变量之前。

        6
  •  2
  •   Hans Passant    14 年前

    你把注意力集中在错误的问题上。ThreadAbortException同样可能会中止OpenText()方法。您可能希望它能够适应这种情况,但事实并非如此 具有尝试处理线程中止的try/catch子句。

    请注意,文件不会永远保持打开状态。文件流终结器最终将关闭文件句柄。当然,当您继续运行并尝试在终结器运行之前再次打开文件时,这仍然会导致程序出现异常。尽管当你在多任务操作系统上运行时,这是你必须时刻保持警惕的事情。

        7
  •  2
  •   Jordão    13 年前

    这本书的作者是对的,using语句是不安全的,还是他们错了,它的行为就像我的第二个解决方案一样?

    根据这本书(第856页),ThreadAbortException可以在托管代码中的任何地方抛出。但也许有例外,第一种变体毕竟是中止安全的?

    using 块不是中止安全的。您的第二个解决方案也不是中止安全的,线程可能在资源获取过程中中止。

    虽然它不是中止安全的,但是任何具有未经整理的资源的可弃文件也应该实现终结器,它将 最后 运行并清理资源。终结器应该足够健壮,以处理未完全初始化的对象,以防线程在资源获取过程中中止。

    一个 Thread.Abort 将只等待在受限执行区域(cer)内运行的代码, finally 阻碍, catch 块、静态构造函数和非托管代码。所以这是一个中止安全的解决方案( 关于资源的获取和处置:

    StreamReader reader = null;
    try {
      try { }
      finally { reader = File.OpenText("file.txt"); }
      // ...
    }
    finally {
      if (reader != null) reader.Dispose();
    }
    

    但是要 仔细的 ,应运行中止安全代码 快速的 不阻塞

    如果using等同于第一个变量(不是abort-safe),为什么它最后要检查null?

    检查空值会使 使用 图案安全 null

        8
  •  0
  •   smirkingman    14 年前

    前者确实完全等同于后者。

    如前所述,ThreadAbort确实是一件坏事,但它与使用任务管理器终止任务或关闭PC机并不完全相同。

    也就是说,一旦你进入ThreadAbort,为什么还要费心清理呢?不管怎么说,你已经死定了。

        9
  •  -2
  •   Tokk    14 年前

    finally语句总是被执行, MSDN

    因此,您不必担心不清理资源等(仅当发生windows、框架运行时或其他无法控制的错误时,但随后会出现比清理资源更大的问题;-))