使用Thread.Abort()有什么问题


Answers:


86

除了这里所有其他好的答案之外,让我补充一点,这不能保证对Thread.Abort的调用实际上会永远终止所讨论的线程。有可能(尽管不是特别容易)“加固”线程以防线程中止。例如,如果您因为认为线程正在运行恶意代码而中止了线程,那么恶意代码可能会抵制自己的破坏。

如果您有长时间运行的涉及不属于您的代码的操作,必须将其彻底删除,那么正确的方法是将该代码放入自己的进程中,而不是放在自己的线程中。(最好是在该过程中受高度安全性限制的应用程序域中。)然后,您可以清除该过程。

简而言之,Thread.Abort最多表示不良设计,这可能是不可靠的,并且非常危险。应不惜一切代价避免这种情况;您甚至应该考虑中止线程的唯一时间是某种“紧急关闭”代码,在该代码中,您试图尽可能干净地关闭应用程序域。


我有一种您强调的情况。这是别人的代码(IronPython控制台),它具有一个Run(...)函数,预计将在线程中运行。顺便说一句,你有一个“强化”线程的例子吗?
Jan Bannister

10
在“受约束的执行区域”上进行网络搜索,您将学到比想了解的更多有关如何处理线程异常中止异常传播的知识。
Eric Lippert

您不应该只是AppDomain.Unload用来拆除应用程序域吗?AppDomain.Unload将为您调用Thread.Abort域中运行的所有线程。
Jordão酒店

1
啊,我明白了,您使用Thread.Abort它是为了避免CannotUnloadAppDomainException中止需要花费一些时间。通过使用Thread.Abort,您可以等待线程真正中止,然后再调用AppDomain.Unload
–Jordão

出于兴趣,您是否为重定向设计了ASP.NET WebForms thread.Abort(),以避免在页面的其余部分运行代码?:D
Quibblesome

20

因为如果您知道线程处于可以退出的安全状态,那么可以肯定地可以安排更好的通信并让线程干净退出。

线程可能已经锁定并且处于更改某些共享状态的中间,并且Thread.Abort将撤消该锁定并使共享状态损坏。


1
可以通过使用msdn.microsoft.com/en-us/library/…来防止这种情况,尽管我认为这很脆弱!
罗布·丰塞卡·恩索2009年

可以在try / final中包装线程的主体,它们可以在finally块中清理资源。但总的来说,我明白你的意思。
Jan Bannister

@ RobFonseca-Ensor不只是脆弱。您是否曾经尝试编写中止安全代码?它使单纯的线程安全性显得微不足道。在Abort可能发生的任何地方(在托管代码) -即使lock语句本身实际上是不放弃安全。因此,您不仅可能处于损坏状态,而且实际上可能lock无法释放它-当然,该线程将在线程死亡时被释放,但实际上并不需要发生(直到该过程为止)死亡)。
Lu安2015年

@JanBannister这比您想象的要难得多-您必须意识到中止可能发生在代码的任何部分,甚至是您不希望任何异常的部分。您真的可以编写一个finally可以正确处理所有内容的子句吗?当然,通过避免共享状态,而仅在安全,受约束的区域中更新共享状态,可以使工作变得更加容易……但是,您现在已经可以不需要显式共享状态了。
a安2015年

14

伤害自己更容易。正如其他人所说,它在代码中引发了一个异常,该异常可能在任何时候发生。如果您期望这样做并且以某种方式可以随时优雅地处理此异常的方式进行了编码,但有些人却没有,那就可能很好:

Monitor.Enter(obj);
// some code - if exception is raised here, then the lock isn't released
Monitor.Exit(obj)

IDisposable someCriticalResource = GetResource();
// some code - if exception is raised here, then the object isn't disposed
someCriticalResource.Dispose();

此外,如果您与团队中的许多人一起工作,除非您对代码进行了良好的审查,否则您不能保证将使用的代码的质量。因此,鼓吹“ no Thread.Abort()”的福音是一个好主意,而不是让人们记住编写针对该代码中任何地方发生的异常情况都健壮的代码。


5
这里重要的一点也不using中止安全-在创建实例和try-finally块的开始之间有一个(很小的)机会窗口。它是微妙的错误的一大秘方,你将永远不会成为能够重现,并且可以很容易地破坏你的数据,挂或杀死你的应用程序等等
Luaan

10

简而言之。不可丢弃任何IDisposable对象。任何锁定的对象都可能无法解锁。任何必须100%执行的操作将永远不会完成。


11
由于停电,任何必须100%执行的操作迟早都会失败...
gimpf

6
这是不正确的,Abort将执行finally块,因此将IDisposable由于相同的原因也将释放所有锁。
Sriram Sakthivel

1
@ByteBlast是什么意思?using语句在这里无关紧要。密钥finally将运行。不管您是否使用using语句。
Sriram Sakthivel 2014年

4
@SriramSakthivel:我要进行此更正很晚,但总比没有好。最后,只有输入try才运行块using(acquire) body;{ acquire; try { body } finally { dispose }}。如果中止发生在获取步骤之后但try输入之前呢?或者,更糟糕的是:如果获取步骤是new Foo()在构造函数中打开文件句柄并将其分配给字段的方法,那该怎么办?如果中止发生在公开赛之后但分配前呢?这个答案是绝对正确的。中止会导致可处置资源无法处置。
埃里克·利珀特

2
@SriramSakthivel:同样,只有在finally运行时才释放锁。在C#4之前,有可能在try块之前输入了监视器。在C#4中,我们更改了代码生成以防止这种可能性。但是请记住,有些人写监视器输入/输出对时没有进行最后的尝试,我认为这实际上更安全。中止确实可以导致监视器进入但从未退出。但这实际上不是坏情况。糟糕的情况是,我们进入了锁定状态,改变了状态以违反不变式,在修复,解锁和暴露突变之前线程中止了!
埃里克·利珀特

5

当您在另一个线程上调用Thread.Abort()时,该线程流中将注入ThreadAbortException。如果幸运的话,代码可以很好地处理并在定义好的状态中止。问题在于您无法弄清楚是否在每种情况下都会很幸运,因此,如果您更喜欢安全而不是遗憾地在其他线程上调用Thread.Abort,则不是一个好主意。


3

Thread.Abort以不受控制的方式停止线程。thread.Abort将引发异常,这将导致您的线程立即停止。

这是怎么回事:在大多数情况下,您希望适当地停止正在执行的操作。例如,如果您正在执行ACID操作,则可能要在结束线程之前完成当前操作,以使系统保持稳定状态。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.