如何有效地杀死 C# 中的线程?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12468734/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to kill a thread in C# effectively?
提问by AngryHacker
I am not tryingto beata deadhorse, honestly. And I've read all the advice on thread killing, however, please consider the code. It does the following:
我不是想给打一个死马,诚实。我已经阅读了关于线程终止的所有建议,但是,请考虑代码。它执行以下操作:
- It starts a thread (via
StartThreadmethod) - It calls the database looking for anything in the ServiceBroker queue. Note the
WAITFORcommand - it means that it will sit there until there is something in the queue. All this inMonitorQueuemethod. Kill the thread. I tried
.Interrupt- it seems to do absolutely nothing. Then I tried.Abort, which should never be used, but even that did nothing.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(); }
- 它启动一个线程(通过
StartThread方法) - 它调用数据库查找 ServiceBroker 队列中的任何内容。请注意该
WAITFOR命令 - 这意味着它将一直坐在那里,直到队列中有东西为止。这一切都在MonitorQueue方法中。 杀死线程。我试过了
.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(); }
Is it actually possible to kill a thread?
真的可以杀死一个线程吗?
采纳答案by Martin James
Set an Abort flag to tell the thread is needs to terminate. Append a dummy record to the ServiceBroker queue. The WAITFOR then returns. The thread then checks its 'Abort' flag and, finding it set, deletes the dummy record from the queue and exits.
设置一个 Abort 标志来告诉线程需要终止。将虚拟记录附加到 ServiceBroker 队列。WAITFOR 然后返回。然后线程检查它的“中止”标志,发现它被设置,从队列中删除虚拟记录并退出。
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. 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. 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? No Abort(), no Interrupt() and, hopefully, no lockup-generating Join().
另一种变体是将“真实”毒丸记录添加到 ServiceBroker 监视的表的规范中 - 非法记录号等。这将完全避免以任何直接方式接触线程 - 总是一件好事:) 这可能更复杂,特别是如果每个工作线程都期望在实际终止时通知,但如果工作线程仍然有效, ServiceBroker 和 DB 都在不同的盒子上。我将其添加为编辑,因为考虑了更多,它似乎更灵活,毕竟,如果线程通常仅通过通信。DB,为什么不只用 DB 关闭它们呢?没有 Abort(),没有 Interrupt(),希望没有产生锁定的 Join()。
回答by Furqan Safdar
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#?
请参考讨论同一件事的这个问题: How to kill a thread in C#?
回答by Dave Markle
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. You see this overand overagain in patterns where you have to wait. The tradeoff is that you don't immediatelyget the thread to die when requested -- you have to wait for your timeout to expire before your thread dies.
我不想不回答你的问题,但考虑以不同的方式解决这个问题。T-SQL 允许使用 WAITFOR 指定 TIMEOUT 参数,这样如果在特定时间段内没有收到消息,则语句将退出并必须再次尝试。你看这个了,并在在,你必须等待模式一次。权衡是您不会在收到请求时立即让线程死亡——您必须在线程死亡之前等待超时到期。
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 sometimesyou don't even wait).
尽管这感觉像是轮询队列,但实际上并非如此。当您轮询时,您正在积极检查某些内容,然后您正在等待避免“旋转”情况,即您不断消耗 CPU(尽管有时您甚至不等待)。
Consider what happens in a polling scenario when you check for entries, then wait 500ms. If nothing's in the queue and 200ms later a message arrives, you have to wait another 300ms when polling to get the message. With a timeout, if a message arrives 200ms into the timeout of the "wait" method, the message gets processed immediately.
考虑在轮询场景中检查条目时会发生什么,然后等待 500 毫秒。如果队列中没有任何内容并且 200 毫秒后消息到达,则轮询时您必须再等待 300 毫秒才能获取消息。对于超时,如果消息到达“等待”方法超时 200 毫秒,则消息会立即得到处理。
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. 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.
轮询时等待强制的时间延迟与在紧密循环中轮询时持续高 CPU 的时间延迟是轮询通常不令人满意的原因。等待超时没有这样的缺点——唯一的折衷是您必须等待超时到期,然后线程才能死亡。
回答by John Gardner
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.
是的,waitfor 的“全部意义”是等待某些东西。但是,如果您希望某事具有响应性,则不能要求一个线程等待 Infinity,然后再期望它听取其他任何内容。
回答by Justin
Don't do this! Seriously!
不要这样做!严重地!
The function that you need to call to kill a thread is the TerminateThreadfunction, which you can call via P/Invoke. All the reasons as to why you shouldn't use this method are right there in the documentation
杀死线程需要调用的TerminateThread函数是可以通过P/Invoke 调用的函数。关于为什么不应该使用此方法的所有原因都在文档中
TerminateThread is a dangerous functionthat should only be used in the most extreme cases. 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. For example, TerminateThread can result in the following problems:
- 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.
- 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.
TerminateThread 是一个危险的函数,只能在最极端的情况下使用。只有当您确切地知道目标线程在做什么,并且您控制目标线程在终止时可能正在运行的所有代码时,才应该调用 TerminateThread。例如,TerminateThread 可能会导致以下问题:
- 如果目标线程拥有临界区,则不会释放临界区。
- 如果目标线程正在从堆中分配内存,则不会释放堆锁。
- 如果目标线程在终止时正在执行某些 kernel32 调用,则该线程进程的 kernel32 状态可能不一致。
- 如果目标线程正在操纵共享 DLL 的全局状态,则 DLL 的状态可能会被破坏,从而影响 DLL 的其他用户。
The important thing to note is the bit in bold, and the fact that under the CLR / .Net framework you are neverin 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. Other people have already posted alternative suggestions on how to achieve this.
如果您找不到某种方法来中止连接,那么您最好让该线程在后台运行,而不是尝试使用TerminateThread. 其他人已经发布了有关如何实现这一目标的替代建议。
The Thread.Abortmethod is slightlysafer in that it raises a ThreadAbortExceptionrather 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.Abortisn'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.Abortalso has its own problems anyway and is generally considered a bad thing to be doing, however it probablywont completely hose your process (although it still might, depending on what the code running is doing).
Thread.Abort无论如何也有它自己的问题,通常被认为是一件坏事,但是它可能不会完全控制你的过程(尽管它仍然可能,取决于运行的代码正在做什么)。

