从ThreadPool调用线程上的Thread.Abort

我的同事正在使用第三方.NET库,我们没有源代码。 我们正在使用ThreadPool让很multithreading调用到这个库中,偶尔其中一个线程将永远挂起,而其余的线程会随意地进行调整。

所以我们想使用可怕的Thread.Abort来杀死这样的线程。 我在开始自己的线程之前已经完成了这个,但我从未使用过ThreadPool。 如果我们跟踪每个任务的开始时间,如下所示:

 static Dictionary started = new Dictionary(); static void DoSomeWork(object foo) { lock(started) started[Thread.CurrentThread] = DateTime.Now; SomeBuggyLibraryThatMightInfiniteLoopOrSomething.callSomeFunction(doo); lock(started) started.Remove(Thread.CurrentThread); } 

那么我们可以锁定并迭代正在运行的线程并调用Thread.Abort来杀死它们吗? 如果我们这样做,那么我们是否需要在ThreadPool中添加一个新线程来替换我们刚刚杀死的线程,或者ThreadPool会为我们处理它?

编辑:我非常清楚Thread.Abort的所有潜在问题。 我知道理想情况下它应该永远不会在生产代码中使用,并且它不一定会停止线程,并且如果你在线程获得锁定时中止线程,那么你可以挂断其他线程等。但是现在我们处于一个紧迫的截止日期,我们有理由相信,在这个特殊情况下,我们可以调用Thread.Abort而不会使整个过程处于危险之中,我们希望避免重写此程序以消除除非我们绝对必须,否则ThreadPool。

所以我想知道的是:假设我们将在一个属于ThreadPool的线程上调用Thread.Abort ,这些是ThreadPool线程引起的任何特殊问题,我们是否必须手动启动一个新线程替换被杀死的那个或者ThreadPool会为我们做那个吗?

不,你不应该在线程池中的线程上调用Abort。 从我的本地测试来看,如果你中止它们,ThreadPool似乎会重新创建线程 – 我中止了1000个线程池线程并且它仍在工作。 我不知道你是否应该依赖这种行为,但也许你可以在这种情况下逃脱它。 通常虽然使用Thread.Abort不是正确的方法。

调用您不信任的函数以表现良好的正确方法是在新进程中启动它并在必要时终止该进程。

不建议使用Thread.Abort,因为它可能使您的应用程序处于无效状态。 这样做的原因是,当要求线程中止时,可以在该第三方库中的几乎任何可能的位置引发exception。 可能存在未编写的代码段以能够应对这些异步中止。

因此,虽然通常不建议中止线程,但是有些主机在中止线程方面非常积极。 其中之一是ASP.NET。 当请求花费太长时间时,它将为您中止该线程。 所以考虑到这一点,说“永不中止线程”是愚蠢的。

我建议你找出这段代码挂起的地方(ThreadAbortException的堆栈跟踪应该给你很多信息)。 如果它总是挂在同一个地方(它可能是死锁),如果在该点中止一个线程将导致某些状态损坏,请找出Reflector。 有了这些信息,您可能已经解决了问题(也许您锁定了该库的对象),或者可以向该库的编写者发送邮件。 如果这一切都没有帮助,你看到没有中止它的风险,务实并杀死它:-)

但是,如果任何州腐败发生变化,您应该尝试使用Mark Byers的答案。 那就是:尝试在自己的AppDomain中运行该库。 这样您就可以卸载完整的AppDomain,并且它不会影响您的应用程序。

为了澄清Mark所说的内容,如果你调用Thread.Abort,由于ThreadAbortException特殊性,你不知道它会在第三方组件中中止 – 例如,它可以让FileStream打开状态。

我个人自己创建线程,在IList或队列中引用(因为ThreadPool更适合于fire和忘记或WaitHandles ),并且取决于你是否认为中止正在使用第三方组件的线程并不危险, Abort它。

如果您认为中止可能会使第三方库处于不可接受的状态,请在一段时间后使用System.Threading.Timer 加入未完成的线程。

另外

要坚持使用ThreadPool,您可以使用此智能线程池 。

阅读Stephen Toub 'Abortable Thread Pool' 。 他提供了一个可中止的线程池的源代码。 这是一个有趣的读物。

在排队线程池时,他调用自己的回调’HandleItem’。 在’HandleItem’中,然后在将当前线程添加到其包装类中的字典列表之后执行实际回调。

 ThreadPool.QueueUserWorkItem(new WaitCallback(HandleItem)); HandleItem(...) { ... _threads.Add(item, Thread.CurrentThread); ... } 

他使用Dictionary将他的WorkItems链接到线程,当用户想要取消线程时,它会查找并取消该特定线程。

 Dictionary() _threads = New Dictionary(); 

http://msdn.microsoft.com/en-us/magazine/cc163644.aspx

你有另一个选择(如果我在我面前有一个免费的日子,我会采取这个选择):

使用reflection器对第三方组件进行反向工程,并实际修复阻塞调用以使其异步。