【问题标题】:Killing a Thread in C#在 C# 中杀死一个线程
【发布时间】:2012-06-01 12:29:29
【问题描述】:

这不是终止系统进程,而是杀死“我自己”。我有几个并行的线程,由于不同的原因,它们可能会挂起。

当线程耗时过长时,我已经创建了一个看门狗:

TimerCallback timerDelegate = new TimerCallback(CheckProcessStatus);
System.Threading.Timer watchDogTimer = new Timer(timerDelegate, new ProcessHealth(plog), 1000 * 60, 1000 * 60);
try
   {
   // lots of code here
   } finally
   {
      watchDogTimer.Dispose();
   }

看门狗:

public void CheckProcessStatus(Object timerState) {
    ProcessHealth ph = (ProcessHealth)timerState;
    System.writeLine(string.Format("process runs for {0} minutes!", ph.WaitingTime);
    if (ph.WaitingTime>60) {
      // KILL THE PROCESS
    }
}

当“这里有很多代码”花费太长时间时,我想终止线程,无论它处于什么状态。(在“终止进程”处)。

最好的方法是什么?

Thread.CurrentThread.Interrupt()

Thread.CurrentThread.Abort()?

或者有更好的方法吗? (我不能使用像布尔“停止”变量这样的“简单”机制,因为“这里有很多代码”非常动态地通过反射等调用其他类。

这还有效吗?还是我只是杀死看门狗线程,而不是要监视的线程?

【问题讨论】:

  • 抛出异常?你的 threadFunc 的“goto”结束似乎比残酷地杀死线程更好。
  • 半相关:stackoverflow.com/q/10827930/15541(估计可以多线程调整)
  • Environment.FailFast() 是结束痛苦的一种非常有效的方法。做任何诸如中止线程之类的事情只会在你已经得到的错误之上堆积更多的错误。
  • volatile bool 方法不适合(就像我在上一段中写的那样)

标签: c# multithreading


【解决方案1】:

Thread.Abort 尝试通过注入带外(异步)异常来终止目标线程。这是不安全的,因为异常会在执行序列中不可预测的点注入。由于对数据结构的写入中断,这可能(并且经常会)导致应用程序域中的某种类型的损坏。

Thread.Interrupt 会导致 BCL 中的大多数阻塞调用(如 Thread.SleepWaitHandle.WaitOne 等)立即退出。与中止线程不同,中断线程可以完全安全,因为异常注入到执行序列中的可预测点。狡猾的程序员可以确保这些点被认为是“安全点”。

所以,如果“这里有很多代码”会响应Thread.Interrupt,那么这可能是一种可以接受的使用方法。但是,我想引导您更多地采用合作取消模式。基本上,这意味着您的代码必须定期轮询取消请求。 TPL 已经通过CancellationToken 建立了一个框架来执行此操作。但是,您可以使用 ManualResetEvent 或简单的 volatile bool 变量轻松完成相同的操作。

现在,如果“这里有很多代码”不受您的控制,或者如果合作取消模式不起作用(可能是因为您使用了有问题的 3rd 方库),那么您几乎别无选择,只能启动一个完全独立的过程来运行有风险的代码。使用 WCF 与进程通信,如果它没有响应,那么您可以在不破坏主进程的情况下杀死它。工作量很大,但它可能是您唯一的选择。

【讨论】:

    【解决方案2】:

    当线程处于未知状态时中止它是不可取的。比如说,线程当前正在执行一个静态构造函数。静态 ctor 将被中止并且永远不会再次运行(因为出错的静态 ctor 永远不会再次运行)。您已经有效地破坏了 AppDomain 中的全局状态,而无法恢复。

    还有很多其他危险。就是不会飞。

    有两个生产就绪选择中止线程:

    1. 协同(结合 Thread.MemoryBarrier 设置事件或布尔标志)
    2. 不要中止线程,而是整个 AppDomain 或进程。线程级粒度太小。您需要删除与该线程相关的所有状态,doo。

    我想强调的是,你不能以任何其他方式完成这项工作。如果你坚持不合作地中止线程,你将在生产中遇到最奇怪的错误。

    【讨论】:

    • 我通常会同意,但这是一项必须运行大量并行线程的服务。这工作得很好,但有时一个线程每月只挂起一次。我们捕获所有异常,甚至知道它发生在哪一行,但无法避免这种行为。我知道我正在寻找一个肮脏的解决方案,但别无选择 另外:线程粒度在我们的例子中是可以的,因为线程是由数据库条目动态启动的,通过反射创建对象。所以这些线程是完全分开的。
    • @OleAlbers - 看来您正在尝试解决错误的问题。而不是解决你的线程挂起的原因你试图解决“如何杀死被挂起的线程”的问题。
    • 好的,如果你知道正在运行的代码,你可以安全地杀死线程,因为它是协作的。使用 Interrupt 来中断挂起的同步,并使用 About 来处理其他所有事情。也许您可以发布挂起的代码?
    猜你喜欢
    • 1970-01-01
    • 2010-11-22
    • 1970-01-01
    • 2022-11-29
    • 1970-01-01
    • 2011-06-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多