我一直在MSDN上四处寻找,找不到找不到在finally块中休眠时无法中断Thread的原因。我尝试中止没有成功。
在finally块中休眠时,有什么方法可以唤醒线程?
Thread t = new Thread(ProcessSomething) {IsBackground = false};
t.Start();
Thread.Sleep(500);
t.Interrupt();
t.Join();
private static void ProcessSomething()
{
try { Console.WriteLine("processing"); }
finally
{
try
{
Thread.Sleep(Timeout.Infinite);
}
catch (ThreadInterruptedException ex)
{
Console.WriteLine(ex.Message);
}
}
}
令人惊讶的是,MSDN声称线程可以在finally块中中止:http://msdn.microsoft.com/en-us/library/aa332364(v=vs.71).aspx
“运行finally块时线程有可能中止,在这种情况下,finally块将中止。”
编辑
我发现Hans Passant的评论是最好的答案,因为这解释了为什么Thread在finally块中有时可以或不能被中断/中止。那就是进程关闭的时候。
谢谢
最佳答案
如果可能,应避免中止和中断线程,因为这会破坏正在运行的程序的状态。例如,假设您中止了一个线程,该线程持有对资源开放的锁,那么这些锁将永远不会被释放。
相反,请考虑使用信令机制,以便线程可以相互协作,从而优雅地处理阻塞和解除阻塞,例如:
private readonly AutoResetEvent ProcessEvent = new AutoResetEvent(false);
private readonly AutoResetEvent WakeEvent = new AutoResetEvent(false);
public void Do()
{
Thread th1 = new Thread(ProcessSomething);
th1.IsBackground = false;
th1.Start();
ProcessEvent.WaitOne();
Console.WriteLine("Processing started...");
Thread th2 = new Thread(() => WakeEvent.Set());
th2.Start();
th1.Join();
Console.WriteLine("Joined");
}
private void ProcessSomething()
{
try
{
Console.WriteLine("Processing...");
ProcessEvent.Set();
}
finally
{
WakeEvent.WaitOne();
Console.WriteLine("Woken up...");
}
}
更新
非常有趣的低级问题。尽管已记录
Abort()
,但Interrupt()
却少得多。对您的问题的简短回答是“否”,您无法通过在其上调用
Abort
或Interrupt
唤醒finally块中的线程。不能中止或中断finally块中的线程是设计使然,只是为了使finally块有机会按您期望的那样运行。如果您可以中止线程并最终中断线程,则可能会对清理例程产生意想不到的后果,从而使应用程序处于损坏状态-不好。
线程中断的一个细微差别是,线程可能在进入finally块之前的任何时间都已针对该线程发出了中断,但是该中断并未处于
SleepWaitJoin
状态(即未阻塞)。在这种情况下,如果在finally块中有一个阻塞调用,它将立即抛出一个ThreadInterruptedException
并从finally块中退出。最后,块保护可以防止这种情况。除了对finally块进行保护外,它还扩展到try块以及CER(Constrained Execution Region),可以在用户代码中对其进行配置,以防止在执行区域之前引发一系列异常-对于必须执行代码的关键代码块非常有用完成并延迟中止。
异常(exception)情况(无双关语)称为Rude Aborts。这些是CLR托管环境本身提出的
ThreadAbortExceptions
。这些可能最终导致捕获块被退出,但CER却无法退出。例如,CLR可能会响应于判断为需要花费很长时间才能完成其工作\退出的线程来提高Rude Aborts。尝试卸载AppDomain或在SQL Server CLR中执行代码时。在您的特定示例中,当您的应用程序关闭且AppDomain卸载时,CLR会在 sleep 线程上发出粗鲁中止,因为会有AppDomain卸载超时。在用户代码中不会发生final块中的中断和中断,但是这两种情况之间的行为略有不同。
中止
在finally块中的线程上调用
Abort
时,调用线程被阻塞。这是documented:The thread that calls Abort might block if the thread that is being aborted is in a protected region of code, such as a catch block, finally block, or constrained execution region.
在中止情况下,如果 sleep 不是无限的:
Abort
,但在此处阻塞,直到退出finally块为止,即,它在此处停止并且不立即进入Join
语句。 AbortRequested
。 AbortRequested
,它将继续执行finally块代码,然后“蒸发”,即退出。 Aborted
。 Join
语句,并在被调用线程退出时立即通过。 因此,在您的示例中具有无限 sleep 的情况下,调用线程将在步骤1永远阻塞。
打断
在中断情况下,如果 sleep 不是无限的:
没有很好的记录...
Interrupt
并继续执行。 Join
语句上阻塞。 ThreadInterruptedException
(请参见下面的代码示例)。 ThreadInterruptedException
现在使进程变平... 因此,再次给您一个无限 sleep 的示例,调用线程将永远阻塞,但在步骤2中。
概括
因此,尽管
Abort
和Interrupt
的行为略有不同,但它们都将导致被调用线程永远处于休眠状态,并且调用线程永远处于阻塞状态(在您的示例中)。只有粗鲁的中止才能迫使被阻塞的线程退出finally块,并且这些只能由CLR本身引发(您甚至不能使用反射来欺骗
ThreadAbortException.ExceptionState
,因为它进行内部CLR调用以获取AbortReason
-没有机会被在那里容易邪恶...)。CLR防止用户代码导致为我们自己的利益而提前退出finally块-它有助于防止损坏状态。
有关
Interrupt
的行为略有不同的示例:internal class ThreadInterruptFinally
{
public static void Do()
{
Thread t = new Thread(ProcessSomething) { IsBackground = false };
t.Start();
Thread.Sleep(500);
t.Interrupt();
t.Join();
}
private static void ProcessSomething()
{
try
{
Console.WriteLine("processing");
}
finally
{
Thread.Sleep(2 * 1000);
}
Console.WriteLine("Exited finally...");
Thread.Sleep(0); //<-- ThreadInterruptedException
}
}
关于c# - 为什么在finally block 中休眠时线程不被中断,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7064811/