简体   繁体   English

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

[英]How to kill a thread in C# effectively?

I am not trying to beat a dead horse , honestly . 老实说 ,我并不是 打败 一匹 And I've read all the advice on thread killing, however, please consider the code. 我已经阅读了有关线程查杀的所有建议,但请考虑代码。 It does the following: 它执行以下操作:

  1. It starts a thread (via StartThread method) 它启动一个线程(通过StartThread方法)
  2. It calls the database looking for anything in the ServiceBroker queue. 它调用数据库查找ServiceBroker队列中的任何内容。 Note the WAITFOR command - it means that it will sit there until there is something in the queue. 注意WAITFOR命令 - 它意味着它将一直坐在那里直到队列中有东西。 All this in MonitorQueue method. 这一切都在MonitorQueue方法中。
  3. Kill the thread. 杀死线程。 I tried .Interrupt - it seems to do absolutely nothing. 我试过.Interrupt - 它似乎什么也没做。 Then I tried .Abort , which should never be used, but even that did nothing. 然后我尝试了.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(); } 

Is it actually possible to kill a thread? 它真的可以杀死一个线程吗?

I hate to not answer your question, but consider going about this a different way. 我讨厌不回答你的问题,但考虑以不同的方式解决这个问题。 T-SQL allows a TIMEOUT parameter to be specified with WAITFOR, such that if a message is not received in a certain period of time, the statement will quit and have to be tried again. T-SQL允许使用WAITFOR指定TIMEOUT参数,这样如果在特定时间段内未收到消息,则该语句将退出并且必须再次尝试。 You see this over and over again in patterns where you have to wait. 你看这个 ,并在,你必须等待模式一次。 The tradeoff is that you don't immediately get the thread to die when requested -- you have to wait for your timeout to expire before your thread dies. 权衡是你没有立即让线程在请求​​时死亡 - 你必须等待你的超时在你的线程死亡之前到期。

The quicker you want this to happen, the smaller your timeout interval. 您希望这种情况发生得越快,您的超时间隔就越小。 Want it to happen instantly? 想要立即发生吗? Then you should be polling instead. 然后你应该轮流投票。

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

EDIT : 编辑

Although this can feel like polling the queue, it's not really. 虽然这可能像轮询队列一样,但事实并非如此。 When you poll, you're actively checking something, and then you're waiting to avoid a "spinning" condition where you're constantly burning up CPU (though sometimes you don't even wait). 当你进行轮询时,你正在积极地检查某些东西,然后你正在等待避免一个“旋转”状态,你不断烧毁CPU(尽管有时你甚至不等)。

Consider what happens in a polling scenario when you check for entries, then wait 500ms. 考虑在检查条目时轮询方案中发生的情况,然后等待500毫秒。 If nothing's in the queue and 200ms later a message arrives, you have to wait another 300ms when polling to get the message. 如果队列中没有任何内容,并且200ms后消息到达,则在轮询时必须再等待300ms才能获得消息。 With a timeout, if a message arrives 200ms into the timeout of the "wait" method, the message gets processed immediately. 如果超时,如果消息到达“等待”方法超时200ms,则会立即处理消息。

That time delay forced by the wait when polling vs. a constant high CPU when polling in a tight loop is why polling is often unsatisfactory. 当轮询在紧密循环中进行轮询与等待恒定的高CPU时,等待的时间延迟是轮询通常不能令人满意的原因。 Waiting with a timeout has no such disadvantages -- the only tradeoff is you have to wait for your timeout to expire before your thread can die. 等待超时没有这样的缺点 - 唯一的权衡是你必须等待你的超时到期才能让你的线程死掉。

Set an Abort flag to tell the thread is needs to terminate. 设置一个Abort标志来告诉线程需要终止。 Append a dummy record to the ServiceBroker queue. 将伪记录附加到ServiceBroker队列。 The WAITFOR then returns. 然后WAITFOR返回。 The thread then checks its 'Abort' flag and, finding it set, deletes the dummy record from the queue and exits. 然后线程检查其“Abort”标志,并找到它设置,从队列中删除虚拟记录并退出。

Another variant would be to add a 'real' poison-pill record to the specification for the table monitored by the ServiceBroker - an illegal record-number, or the like. 另一种变体是将“真正的”毒丸记录添加到ServiceBroker监控的表的规范中 - 非法记录号等。 That would avoid touching the thread/s at all in any direct manner - always a good thing:) This might be more complex, especially if each work thread is expeceted to notify upon actual termination, but would still be effective if the work threads, ServiceBroker and DB were all on different boxes. 这样可以避免以任何直接方式触及线程 - 总是一件好事:)这可能会更复杂,特别是如果每​​个工作线程都被用来通知实际终止,但是如果工作线程仍然有效, ServiceBroker和DB都在不同的盒子上。 I added this as an edit because, having thought a bit more about it, it seems more flexible, after all, if the threads normally only communicate via. 我添加了这个作为编辑,因为,考虑到它的更多,它似乎更灵活,毕竟,如果线程通常只通过通信。 the DB, why not shut them down with only the DB? DB,为什么不用DB关闭它们呢? No Abort(), no Interrupt() and, hopefully, no lockup-generating Join(). 没有Abort(),没有Interrupt(),并且希望没有生成锁定的Join()。

instead of killing your thread, change your code to use WAITFOR with a small timeout. 而不是杀死你的线程,改变你的代码使用WAITFOR与一个小超时。

after the timeout elapses, check to see if the thread has been interrupted. 超时过后,检查线程是否已被中断。

if not, loop back around and do your waitfor again. 如果没有,回过头来再做一次等待。

Yes, "the entire point" of waitfor is to wait for something. 是的,等待的“整点”是等待某事。 But if you want something to be responsive, you can't ask one thread to wait for Infinity, and then expect it to listen to anything else. 但是,如果你想要一些响应,你不能要求一个线程等待Infinity,然后期望它听其他任何东西。

Don't do this! 不要这样做! Seriously! 认真!

The function that you need to call to kill a thread is the TerminateThread function, which you can call via P/Invoke. 您需要调用以TerminateThread线程的函数是TerminateThread函数,您可以通过P / Invoke调用它。 All the reasons as to why you shouldn't use this method are right there in the documentation 关于为什么不应该使用这种方法的所有原因都在文档中

TerminateThread is a dangerous function that should only be used in the most extreme cases. TerminateThread是一个危险的函数 ,只能在最极端的情况下使用。 You should call TerminateThread only if you know exactly what the target thread is doing, and you control all of the code that the target thread could possibly be running at the time of the termination . 只有当您确切知道目标线程正在执行的操作时,才应调用TerminateThread,并且您可以控制目标线程在终止时可能正在运行的所有代码 For example, TerminateThread can result in the following problems: 例如,TerminateThread可能会导致以下问题:

  • If the target thread owns a critical section, the critical section will not be released. 如果目标线程拥有临界区,则不会释放临界区。
  • If the target thread is allocating memory from the heap, the heap lock will not be released. 如果目标线程正在从堆中分配内存,则不会释放堆锁。
  • If the target thread is executing certain kernel32 calls when it is terminated, the kernel32 state for the thread's process could be inconsistent. 如果目标线程在终止时正在执行某些kernel32调用,则线程进程的kernel32状态可能不一致。
  • If the target thread is manipulating the global state of a shared DLL, the state of the DLL could be destroyed, affecting other users of the DLL. 如果目标线程正在操纵共享DLL的全局状态,则DLL的状态可能会被破坏,从而影响DLL的其他用户。

The important thing to note is the bit in bold, and the fact that under the CLR / .Net framework you are never in the situation where you know exactly what the target thread is doing (unless you happen to write the CLR). 需要注意的重要事项是粗体位,以及在CLR / .Net框架下,您永远不会知道目标线程正在做什么的情况(除非您碰巧编写CLR)。

To clarify, calling TerminateThread on a thread running .Net code could quite possibly deadlock your process or otherwise leave in a completely unrecoverable state . 为了澄清, 在运行.Net代码的线程上调用TerminateThread很可能会使您的进程陷入僵局,或者处于完全不可恢复的状态

If you can't find some way to abort the connection then you are far better off just leaving that thread running in the background than trying to kill it with TerminateThread . 如果你找不到某种方法来中断连接,那么你最好不要让那个线程在后台运行,而不是试图用TerminateThread来杀死它。 Other people have already posted alternative suggestions on how to achieve this. 其他人已经发布了关于如何实现这一目标的替代建议。


The Thread.Abort method is slightly safer in that it raises a ThreadAbortException rather than immediately tearing down your thread, however this has the disadvantage of not always working - the CLR can only throw the exception if the CLR is actually running code on that thread, however in this case the thread is probably sat waiting for some IO request to complete in native SQL Server Client code instead, which is why your call to Thread.Abort isn't doing anything, and won't do anything until control is returned to the CLR. Thread.Abort方法稍微安全一点,因为它引发ThreadAbortException而不是立即拆除你的线程,但是这样做的缺点是不能总是工作 - 如果CLR实际上在该线程上运行代码,CLR只能抛出异常,但是在这种情况下,线程可能正在等待一些IO请求在本机SQL Server客户端代码中完成,这就是为什么你对Thread.Abort的调用没有做任何事情,并且在控制返回之前不会做任何事情。 CLR。

Thread.Abort also has its own problems anyway and is generally considered a bad thing to be doing, however it probably wont completely hose your process (although it still might, depending on what the code running is doing). Thread.Abort还有其自身的问题并且通常被认为是一件坏事,但它可能不会完全阻塞你的进程(尽管它仍然可能,取决于运行的代码正在做什么)。

It is not just easy to terminate the thread right away. 立即终止线程并不容易。 There is a potential potential problem associated with it: 可能存在与之相关的潜在问题:

Your thread acquire a lock, and then you kill it before it releases the lock. 你的线程获得一个锁,然后在它释放锁之前杀死它。 Now the threads who require the lock will get stuck. 现在需要锁定的线程将被卡住。

You can use some global variable to tell the thread to stop. 您可以使用一些全局变量来告诉线程停止。 You have to manually, in your thread code, check that global variable and return if you see it indicates you should stop. 您必须在线程代码中手动检查全局变量并返回,如果您看到它表明您应该停止。

Please refer to this question discussing the same thing: How to kill a thread instantly in C#? 请参考这个问题讨论同样的事情: 如何在C#中立即杀死一个线程?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM