从ThreadPool调用线程上的Thread.Abort

时间:2010-02-25 15:36:44

标签: c# .net threadpool

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

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

static Dictionary<Thread, DateTime> started = new Dictionary<Thread, DateTime>();

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会为我们处理它?<​​/ p>

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

所以我想知道的是:假设我们将在属于ThreadPool的线程上调用Thread.Abort,那么这些线程是由ThreadPool线程引起的,我们是否需要手动执行启动一个新线程来替换那个被杀死的线程,或者ThreadPool会为我们做这个吗?

5 个答案:

答案 0 :(得分:9)

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

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

答案 1 :(得分:6)

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

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

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

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

答案 2 :(得分:3)

为了澄清Mark说的话,如果你调用Thread.Abort,由于ThreadAbortException的特殊性,你不知道它会在第三方组件中止的位置 - 它可能会留下FileStream例如,打开。

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

如果您认为中止可能会使第三方库处于不可接受的状态,Join使用System.Threading.Timer

替代地

要坚持使用ThreadPool,您可以使用此Smart Thread Pool代替。

答案 3 :(得分:3)

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

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

ThreadPool.QueueUserWorkItem(new WaitCallback(HandleItem));

HandleItem(...) {
...
_threads.Add(item, Thread.CurrentThread);
...
}

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

Dictionary<WorkItem, Thread>() _threads = New Dictionary<WorkItem, Thread>();

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

答案 4 :(得分:1)

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

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