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

老实说 ,我并不是想 打败 一匹 死 马 。 我已经阅读了关于线程查杀的所有建议,但请考虑代码。 它执行以下操作:

  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(); } 

实际上可以杀死一个线程吗?

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

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

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

您希望这种情况发生得越快,您的超时间隔就越小。 想让它立即发生吗? 然后你应该轮流投票。

 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(尽管有时候你甚至都没有等待)。

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

当轮询在紧密循环中进行轮询与等待恒定高CPU时等待的时间延迟是轮询通常不能令人满意的原因。 等待超时没有这样的缺点 – 唯一的权衡是你必须等待你的超时到期才能让你的线程死掉。

而不是杀死你的线程,改变你的代码使用WAITFOR与一个小超时。

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

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

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

不要这样做! 认真!

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

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

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

需要注意的重要事项是粗体位,以及在CLR / .Net框架下,您永远不会知道目标线程正在做什么的情况(除非您碰巧编写CLR)。

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

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


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

Thread.Abort还有其自身的问题,通常被认为是一件坏事,但它可能不会完全阻塞你的进程(尽管它仍然可能,取决于运行的代码在做什么)。

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

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

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

请参考这个问题讨论同样的事情: 如何在C#中立即杀死一个线程?