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

Ang*_*ker 16 .net c# multithreading .net-3.5

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

  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();
    }
    
    Run Code Online (Sandbox Code Playgroud)

它真的可以杀死一个线程吗?

Dav*_*kle 8

我讨厌不回答你的问题,但考虑以不同的方式解决这个问题.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;
}
Run Code Online (Sandbox Code Playgroud)

编辑:

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

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

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

  • 击败了我48秒,你使用了代码.你是赢家! (2认同)

Mar*_*mes 8

设置一个Abort标志来告诉线程需要终止.将伪记录附加到ServiceBroker队列.然后WAITFOR返回.然后线程检查其"Abort"标志,并找到它设置,从队列中删除虚拟记录并退出.

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


Jus*_*tin 5

不要这样做!严重地!

杀死线程所需调用的函数是函数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 客户端代码中完成,这就是为什么您的调用Thread.Abort没有执行任何操作,并且在控制权返回到 CLR 之前不会执行任何操作。

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