C#的使用语句是否中止?

Dzi*_*inX 28 c# multithreading using-statement thread-safety abort

我刚读完"Nutshell中的C#4.0"(O'Reilly),我认为这对于愿意转向C#的程序员来说是一本好书,但它让我感到疑惑.我的问题是using声明的定义.根据这本书(第138页),

using (StreamReader reader = File.OpenText("file.txt")) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

恰好相当于:

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

但是,假设这是真的,并且此代码在单独的线程中执行.此线程现在已中止thread.Abort(),因此ThreadAbortException抛出a并假设线程正好在初始化读取器之后和输入try..finally子句之前.这意味着读者不会被处置!

一种可能的解决方案是以这种方式编码:

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

这将是中止安全的.

现在我的问题:

  1. 这本书的作者是否正确,using声明是不是中止安全还是错误,它的行为与我的第二个解决方案相似?
  2. 如果using等同于第一方案(不放弃安全的),为什么它检查nullfinally
  3. 根据该书(第856页),ThreadAbortException可以在托管代码中的任何位置抛出.但也许有例外,第一个变种毕竟是中止安全的?

编辑:我知道使用thread.Abort()不被认为是好习惯.我的兴趣是纯粹的理论:如何在using声明中表现究竟

Joe*_*ari 17

本书的配套网站对中止线程的详细信息在这里.

简而言之,第一次翻译是正确的(你可以通过查看IL来判断).

第二个问题的答案是,可能存在变量可以合法地为空的情况.例如,GetFoo()可能在这里返回null,在这里你不希望在隐式finally块中抛出NullReferenceException:

using (var x = GetFoo())
{
   ...
}
Run Code Online (Sandbox Code Playgroud)

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


Cly*_*yde 8

两种情况之间确实没有区别 - 在第二种情况下,ThreadAbort仍然可以在调用OpenText之后,但在将结果分配给阅读器之前发生.

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

为了回应你的编辑 - 我会再次指出你的两个场景实际上是相同的.除非File.OpenText调用成功完成并返回一个值,否则'reader'变量将为null ,因此在第一种方式和第二种方式之间编写代码没有区别.


Mar*_*ell 6

Thread.Abort是非常非常糟糕的juju; 如果有人打电话说你已经遇到很多麻烦(不可恢复的锁等).Thread.Abort应该真的只限于吸食病态过程的scanerio.

异常通常是干净地展开,但在极端情况下,无法保证每一段代码都可以执行.一个更紧迫的例子是"如果电源出现故障会怎么样?".

重新null检查; 如果什么File.OpenText回来null?好吧,它不会,编译器不知道.