C#的using语句会中止吗?我刚刚完成了“C#4.0inaNutshell”(O'Reilly),我认为对于愿意转向C#的程序员来说这是一本好书,但它让我感到疑惑。我的问题是using语句的定义。根据本书(第138页),使用(StreamReaderreader=File.OpenText("file.txt")){...}完全等同于:StreamReaderreader=File.OpenText("file.txt");try{...}finally{if(reader!=null)((IDisposable)reader).Dispose();然而,假设这是真的,并且这段代码在一个单独的线程中执行。该线程现在使用thread.Abort()中止,因此抛出ThreadAbortException并假设线程刚好在初始化读取器之后和进入try..finally子句之前。这意味着读者没有被处置!一种可能的解决方案是这样编码:StreamReaderreader=null;尝试{reader=File.OpenText("file.txt");...}最后{if(reader!=null)((IDisposable)reader).Dispose();这将是安全的。现在我的问题是:这本书的作者是正确的,using语句是中止安全的还是错误的,它的行为与我的第二个解决方案相似?如果using等同于第一个变体(不是中止安全的),为什么finally检查null?根据该书(第856页),ThreadAbortException可以在托管代码中的任何位置抛出。但也许有例外,第一个变体毕竟是中止安全的?编辑:我知道使用thread.Abort()不被认为是好的做法。我的兴趣纯粹是理论上的:using语句的行为如何?本书的配套网站提供了有关中止线程的更多信息。简而言之,第一个翻译是正确的(你可以通过查看IL来判断)。第二个问题的答案是,在某些情况下,变量可以合法地为null。例如,GetFoo()可能会在此处返回null,而您不想在隐式finally块中抛出NullReferenceException:这样做的唯一方法(如果您正在调用框架代码)是随后拆除AppDomain。在许多情况下,这实际上是一个实用的解决方案(就像LINQPad在取消正在运行的查询时所做的那样)。这两种情况之间确实没有区别-在第二种情况下,ThreadAbort仍然可以在调用OpenText之后但在将结果分配给读取器之前发生。基本上,当您收到ThreadAbortException时,所有的赌注都会被取消。这就是为什么你不应该故意中止一个线程,而是使用其他方法来优雅地结束它。作为对您编辑的回应-我会再次指出您的两个场景实际上是相同的。除非File.OpenText调用成功完成并返回一个值,否则“reader”变量将为null,因此第一种方式与第二种方式编写代码之间没有区别。Thread.Abort是非常非常糟糕的juju;如果有人打电话说您已经遇到很多麻烦(无法恢复的锁等)。Thread.Abort应限于病态过程中的scanerio。异常通常会干净利落地展开,但在极端情况下,无法保证每段代码都会执行。一个更紧迫的例子是“如果停电了怎么办?”。复查;如果File.OpenText返回null怎么办?嗯,它不会,但编译器不知道。有点跑题,但线程中止期间锁定语句的行为也很有趣。而锁等同于:objectobj=x;System.Threading.Monitor.Enter(obj);尝试{...}最后{System.Threading.Monitor.Exit(obj);}(通过x86JITter)保证在Monitor.Enter和try语句之间不会发生线程中止。http://blogs.msdn.com/b/ericlippert/archive/2007/08/17/subtleties-of-c-il-codegen.aspx.net4中生成的IL代码似乎有所不同:http://blogs.msdn.com/b/ericlippert/archive/2009/03/06/locks-and-exceptions-do-not-mix.aspx语言规范明确指出第一个是正确的。http://msdn.microsoft.com/en-us/vcsharp/aa336809.aspxMS规范(Word文档)http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-334.pdfECMA规范指出,如果线程中止,两种代码变体都可能失败。第二个是中止,它发生在表达式被求值之后但在它被分配给局部变量之前。但是你不应该使用线程中止,因为它很容易破坏appdomain的状态。因此,只有当AppDomain被强制卸载时,线程才会中止。你关注的是错误的问题。ThreadAbortException也可能中止OpenText()方法。您可能期望它是有弹性的,但它不是。框架方法没有尝试处理线程中止的try/catch子句。请注意,该文件不会永远保持打开状态。FileStream终结器最终关闭文件句柄。当然,当你继续运行并在终结器运行之前再次尝试打开文件时,这仍然会导致程序出现异常。尽管这是在多任务操作系统上运行时始终需要防范的事情。这本书的作者是否正确使用语句是中止安全的,或者它的行为与我的第二个解决方案类似是错误的吗?根据该书(第856页),ThreadAbortException可以在托管代码中的任何位置抛出。但也许有例外,第一个变体毕竟是中止安全的?作者是对的。使用块不是中止安全的。您的第二个解决方案也不是中止安全的,线程可以在获取资源的过程中中止。虽然它不是中止安全的,但任何具有非托管资源的一次性资源也应该实现最终运行并清理资源的终结器。终结器应该足够健壮,以在线程在资源获取期间中止的情况下处理未完全初始化的对象。Thread.Abort只会等待在受限执行区域(CER)、finally块、catch块、静态构造函数和非托管代码中运行的代码。所以这里有一个abort-safe的解决方案(只涉及资源的获取和处置):StreamReaderreader=null;尝试{尝试{}最后{reader=File.OpenText("file.txt");}//...}finally{if(reader!=null)reader.Dispose();但要小心,中止安全代码应该快速运行而不是阻塞。它可以暂停整个应用程序域的卸载操作。如果用法等同于第一个变体(不是中止安全的),为什么它最终会检查null?检查null使using模式在存在null引用时是安全的。前者确实完全等同于后者。正如已经指出的那样,ThreadAbort确实是一件坏事,但它与使用任务管理器终止任务或关闭PC并不完全相同。ThreadAbort是一个托管异常,运行时将在可能时引发,并且仅在此时引发。也就是说,一旦进入ThreadAbort,为什么还要清理它呢?不管怎样,你都快死了。finally语句总是被执行,MSDN说“final用于保证语句代码块的执行,而不管前面的try块是如何退出的”。所以你不用担心没有清理资源等问题(只有当windows,Framework-Runtime或者其他任何你无法控制的错误发生的时候,但是接下来会出现比清理资源更大的问题;-))以上就是上面的C#学习教程:C#是否使用语句abort?如果所有分享的内容对你有用,需要进一步了解C#学习教程,希望大家多多关注。本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处:
