【问题标题】:Killing a .NET thread杀死一个 .NET 线程
【发布时间】:2009-06-27 00:38:10
【问题描述】:

我创建了一个运行某个方法的线程。但有时我想杀死线程,即使它仍在工作。我怎样才能做到这一点?我尝试了 Thread.Abort() 但它显示了一个消息框,上面写着“线程中止”。我该怎么办?

【问题讨论】:

  • 您是否阅读了 MSDN 上的“备注”部分?切勿使用中止。这是最后的手段。如果你使用 THREAD.ABORT,SKY 可能会掉下来,小猫会被杀死。
  • 小猫?我以为是鸡 :) 但你的个人资料图片解释了你的评论!
  • 正如其他人所说,尽量不要这样做;您应该尝试永远不要创建一个执行您无法控制的事情的线程。如果你必须杀死你无法控制的正在运行的代码,最安全的做法是在另一个进程中运行代码,而不是另一个线程。关闭进程比关闭线程要安全得多。

标签: c# multithreading abort


【解决方案1】:

Do not call Thread.Abort()!

Thread.Abort 很危险。相反,您应该与线程合作,以便可以和平地关闭它。需要对线程进行设计,以便可以告诉它杀死自己,例如,当您希望线程停止时,将布尔值 keepGoing 设置为 false 标志。然后该线程将具有类似

while (keepGoing)
{
    /* Do work. */
}

如果线程可能在SleepWait 中阻塞,那么您可以通过调用Thread.Interrupt() 将其从这些函数中分离出来。然后线程应该准备好处理ThreadInterruptedException

try
{
    while (keepGoing)
    {
        /* Do work. */
    }
}
catch (ThreadInterruptedException exception)
{
    /* Clean up. */
}

【讨论】:

  • 使用异常处理来控制程序流是一个可怕的想法。请改用 WaitHandle。
  • 我觉得没那么可怕。如果您的循环不够紧凑,并且在最终知道应该退出之前可能需要 30 秒的处理时间,那么异常是停止它的完美方法。
  • 马克,如果你的线程阻塞输入并且你需要让它停止呢?它可以无限期地阻塞,所以中断它并处理异常是让它退出的唯一方法。
  • @Lirik 那么你应该使用非阻塞版本,或者可以取消的异步调用。想想 Unix 中的 select()
  • 那么如何从 SqlCommand.ExecuteNonQuery() 中取出线程呢?不要说在这里调用 Async() 你只是移动了问题。
【解决方案2】:

你真的应该只调用 Abort() 作为最后的手段。你可以使用一个变量来同步这个线程:

volatile bool shutdown = false;

void RunThread()
{
   while (!shutdown)
   {
      ...
   }
}

void StopThread()
{
   shutdown = true;
}

这允许您的线程干净地完成它正在做的事情,让您的应用处于已知的良好状态。

【讨论】:

    【解决方案3】:

    最正确和线程安全的方法是在线程应该停止时使用 WaitHandle 向线程发出信号。我主要使用 ManualResetEvent。

    在您的线程中,您可以:

    private void RunThread()
    {
        while(!this.flag.WaitOne(TimeSpan.FromMilliseconds(100)))
        {
            // ...
        }
    }
    

    其中this.flag 是ManualResetEvent 的一个实例。这意味着您可以从线程外部调用this.flag.Set() 来停止循环。

    WaitOne 方法只会在设置标志时返回 true。否则,在指定的超时时间(本例中为 100 毫秒)后超时,线程将再次循环运行。

    【讨论】:

    • 实现的缺点是,在每个循环中,您都将等待事件,什么也不做。我宁愿相反。具有指示关闭的标志并表示胎面已结束执行。 while (!WillTerminate) {..} HasTerminate.Set();
    • 这可以在BackgroundWorkerDoWork活动中使用吗?
    【解决方案4】:

    杀死一个线程不是一个好主意。最好发出信号让它停止并让它优雅地结束。有多种不同的方法可以做到这一点。

    • 如果被阻止,使用Thread.Interrupt戳它。
    • 轮询标志变量。
    • 使用WaitHandle 类发送信号。

    由于我已经在this answer 中重复了如何使用每种方法,因此我无需重复。

    【讨论】:

    • +1。你能提供一个通过等待句柄杀死/停止线程的简单示例吗?
    【解决方案5】:

    中止线程是一个非常糟糕的主意,因为您无法确定线程在中止时正在做什么。

    相反,拥有一个线程可以检查的属性,并且您的外部代码可以设置该属性。当线程在安全的地方退出时,让线程检查这个布尔属性。

    【讨论】:

      【解决方案6】:

      我同意乔恩 B

      volatile bool shutdown = false;
      
      void RunThread()
      {
      
      try
      {
          while (!shutdown)
          {
              /* Do work. */
          }
      }
      catch (ThreadAbortException exception)
      {
          /* Clean up. */
      }
      }
      
      void StopThread()
      {
         shutdown = true;
      }
      

      【讨论】:

        【解决方案7】:

        我的WebServer类中也有杀死线程的例子……

        https://net7ntcip.codeplex.com/SourceControl/changeset/view/89621#1752948

        我会说 Abort 没问题,只需了解后果是什么......只要您在长时间运行的任务 Abort 工作之前指示状态,但需要标志,例如(ShouldStop 或 ActionBranch 等)

        查看示例!

        【讨论】:

          【解决方案8】:

          编辑:

          1. 我为此创建了一个小类

          2. 注意到它不适用于 async/await

          3. 向班级发布了更新,然后 Theodor Zoulias(谢谢 :))让我知道这个想法有缺陷。

          4. 现在我正在重新发布原始课程(不适用于 async/await!)

            var t = new StopAbleThread(isShutDown =>
             {
               Console.WriteLine("a");
               while (!isShutDown())
               {
                 Console.WriteLine("b");
                 Thread.Sleep(TimeSpan.FromMinutes(10));
               }
             }  )   
            
            { IsBackground = true };
               t.Start( );
            

          像这样停止线程:

          t.Stop();
          

          班级:

          public class StopAbleThread
          {
            public bool IsBackground
            {
              set => _thread.IsBackground = value;
            }
          
            private readonly Thread _thread;
            private volatile bool _shutdown;
          
            public delegate bool IsShutDown( );
            public delegate void CodeToRun( IsShutDown isShutDown );
          
          
            public StopAbleThread( CodeToRun codeToRun )
            {
              _thread = new Thread( ( ) =>
              {
                try
                {
                  codeToRun( _IsShutDown );
                }
                catch( ThreadInterruptedException )
                {
                  //ignore
                }
              } );
            }
          
            private bool _IsShutDown( )
            {
              return _shutdown;
            }
          
            public void Start( )
            {
              _thread.Start( );
            }
          
            public void Stop( )
            {
              _shutdown = true;
              _thread.Interrupt( );
            }
          }
          

          【讨论】:

          • Thread 构造函数不理解异步委托。你可以阅读这个herehere。结果_thread.Interrupt() 将中断一个已经终止的线程,所以它实际上不会做任何事情。
          • 感谢您的评论。上面的代码在我的用例中按预期工作。中断调用确实会在睡眠时唤醒线程。
          • 您的答案的first revison 没问题,因为所有代码都是同步的。通过添加 async/await,您创建了一个无法始终如一地工作的有缺陷的版本。无法保证await 点之后的代码将继续在与等待之前相同的线程上运行。在您的示例中,await Task.Delay(1) 点是_thread 将终止的位置,随后的Thread.Sleep(FromMinutes(10)) 将使ThreadPool 线程而不是_thread 进入休眠状态。然后_thread.Interrupt(); 什么都不做。
          • 不信的话,在await前后登录ConsoleThread.CurrentThread.ManagedThreadId,看看是不是一样。
          • 好的,再次感谢。认为我应该重新发布第一个解决方案?我真的需要一个等待的解决方案。你知道更好的方法吗?
          猜你喜欢
          • 1970-01-01
          • 2022-11-29
          • 1970-01-01
          • 1970-01-01
          • 2012-01-31
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多