如何有效地杀死C#中的线程?

时间:2012-09-18 00:07:01

标签: c# .net multithreading .net-3.5

我不是tryingbeatdead horsehonestly。我已经阅读了有关线程查杀的所有建议,但请考虑代码。它执行以下操作:

  1. 启动一个帖子(通过StartThread方法)
  2. 它调用数据库查找ServiceBroker队列中的任何内容。注意WAITFOR命令 - 这意味着它将一直存在,直到队列中有东西为止。所有这些都在MonitorQueue方法中。
  3. 杀死线程。我试过.Interrupt - 似乎什么也没做。然后我尝试了.Abort,它永远不会被使用,但即使这样做也没有。

    Thread thxMonitor = new Thread(MonitorQueue);
    void StartThread() {
        thxMonitor.Start();
    }
    
    void MonitorQueue(object obj) {
        var conn = new SqlConnection(connString);
        conn.Open();
        var cmd = conn.CreateCommand();
        cmd.CommandTimeout = 0; // forever and ever
        cmd.CommandType = CommandType.Text;
        cmd.CommandText = "WAITFOR (RECEIVE CONVERT(int, message_body) AS Message FROM SBQ)";
    
        var dataTable = new DataTable();
        var da = new SqlDataAdapter(command);
    
        da.Fill(dataTable);
        da.Dispose();
    }
    
    void KillThreadByAnyMeansNecessary() {
        thxMonitor.Interrupt();
        thxMonitor.Abort();
    }
    
  4. 实际上是否有可能杀死一个线程?

5 个答案:

答案 0 :(得分:8)

我讨厌不回答你的问题,但考虑以不同的方式解决这个问题。 T-SQL允许使用WAITFOR指定TIMEOUT参数,这样如果在特定时间段内未收到消息,则该语句将退出并且必须再次尝试。您会在需要等待的模式中再次看到此overover。权衡是你没有立即让线程在请求​​时死亡 - 你必须等待你的超时在你的线程死亡之前到期。

您希望这种情况发生得越快,您的超时间隔就越小。想要立即发生吗?然后你应该进行民意调查。

static bool _quit = false;

Thread thxMonitor = new Thread(MonitorQueue);
void StartThread() {
    thxMonitor.Start();
}

void MonitorQueue(object obj) {

    var conn = new SqlConnection(connString);
    conn.Open();
    var cmd = conn.CreateCommand();
    cmd.CommandType = CommandType.Text;
    cmd.CommandText = "WAITFOR (RECEIVE CONVERT(int, message_body) AS Message FROM SBQ) TIMEOUT 500";

    var dataTable = new DataTable();    

    while(!quit && !dataTable.AsEnumerable().Any()) {
        using (var da = new SqlDataAdapter(command)) {    
            da.Fill(dataTable);
        }
    }
}

void KillThreadByAnyMeansNecessary() {
    _quit = true;
}

修改

虽然这可能像轮询队列一样,但事实并非如此。当你进行轮询时,你正在积极地检查某些东西,然后你正在等待避免一个“旋转”状态,你不断烧毁CPU(尽管sometimes你甚至都没有等待)。

考虑在检查条目时轮询方案中发生的情况,然后等待500毫秒。如果队列中没有任何内容,并且200ms后消息到达,则在轮询时必须再等待300ms才能获得消息。如果超时,如果消息到达“等待”方法超时200ms,则会立即处理消息。

轮询时,等待时间延迟会导致轮询,而在紧密循环中进行轮询时,轮询通常不能令人满意。等待超时没有这样的缺点 - 唯一的权衡是你必须等待你的超时到期才能让你的线程死掉。

答案 1 :(得分:8)

设置Abort标志以告知线程需要终止。将伪记录附加到ServiceBroker队列。然后WAITFOR返回。然后线程检查其“Abort”标志,并找到它设置,从队列中删除虚拟记录并退出。

另一种变体是将“真正的”毒丸记录添加到ServiceBroker监控的表的规范中 - 非法记录号等。这样可以避免以任何直接的方式触及线程 - 总是一件好事:)这可能会更复杂,特别是如果每​​个工作线程都被用来通知实际终止,但是如果工作线程仍然有效, ServiceBroker和DB都在不同的盒子上。我添加了这个作为编辑,因为,考虑到它的更多,它似乎更灵活,毕竟,如果线程通常只通过通信。 DB,为什么不用DB关闭它们呢?没有Abort(),没有Interrupt(),希望没有生成锁定的Join()。

答案 2 :(得分:4)

而不是杀死你的线程,更改你的代码以使用WAITFOR并暂停一小段时间。

超时后,检查线程是否被中断。

如果没有,请回过头来再次等待。

是的,“等待的全部要点”是等待某事。但是如果你想要一些响应,你不能要求一个线程等待无限,然后期望它听其他任何事情。

答案 3 :(得分:4)

不要这样做!认真!

要杀死线程需要调用的函数是TerminateThread函数,可以通过P / Invoke调用它。关于为什么你不应该使用这种方法的所有原因都在文档中

  

TerminateThread是一个危险函数,只能在最极端的情况下使用。 只有在你的情况下才应该调用TerminateThread   确切知道目标线程正在做什么,并控制所有   目标线程当时可能正在运行的代码   终止。例如,TerminateThread可以导致   以下问题:

     
      
  • 如果目标线程拥有临界区,则不会释放临界区。
  •   
  • 如果目标线程正在从堆中分配内存,则不会释放堆锁。
  •   
  • 如果目标线程在终止时正在执行某些kernel32调用,则线程进程的kernel32状态可能是   不一致。
  •   
  • 如果目标线程正在操纵共享DLL的全局状态,则DLL的状态可能会被破坏,影响其他状态   DLL的用户。
  •   

需要注意的重点是粗体位,以及在CLR / .Net框架下,在您确切知道目标线程正在做什么的情况下从不这一事实(除非你碰巧写了CLR。)

为了澄清,在运行.Net代码的线程上调用TerminateThread很可能会使您的进程陷入僵局,或者处于完全无法恢复的状态

如果你找不到中断连接的方法,那么你最好不要让那个线程在后台运行而不是试图用TerminateThread来杀死它。其他人已经发布了关于如何实现这一目标的替代建议。


Thread.Abort方法稍微更安全,因为它会引发ThreadAbortException而不是立即拆除你的线程,但是这样做的缺点是不能一直工作 - CLR如果CLR实际上在该线程上运行代码,则只能抛出异常,但是在这种情况下,线程可能正在等待某些IO请求在本机SQL Server客户端代码中完成,这就是您调用{{1}的原因没有做任何事情,并且在控制权返回给CLR之前不会做任何事情。

Thread.Abort无论如何也有自己的问题并且通常被认为是一件坏事,但它可能不会完全阻止你的过程(虽然它仍然可能,取决于什么代码正在运行。)

答案 4 :(得分:2)

立即终止线程并不容易。可能存在与之相关的潜在问题:

你的线程获得了一个锁,然后你在它释放锁之前杀了它。现在需要锁定的线程将被卡住。

您可以使用一些全局变量来告诉线程停止。您必须在线程代码中手动检查全局变量,如果看到它则返回,表明您应该停止。

请参考这个问题讨论相同的事情: How to kill a thread instantly in C#?