Mar*_*rek 9 c# multithreading interrupted-exception
我一直在寻找MSDN,并且无法找到为什么Thread在finally块中睡眠时无法中断的原因.我试过中止没有成功.
有没有什么方法可以在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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
令人惊讶的是MSDN声称线程可以在最终块中止:http://msdn.microsoft.com/en-us/library/aa332364(v = vs.71).aspx "线程可能会在最终阻塞时中止正在运行,在这种情况下,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...");
}
}
Run Code Online (Sandbox Code Playgroud)
更新
相当有趣的低级问题.尽管Abort()有文献记载,Interrupt()但更不用说了.
对你的问题的简短回答是否定的,你不能通过调用Abort或Interrupt在它上面唤醒finally块中的线程.
无法在finally块中中止或中断线程是设计的,只是为了使最终块有机会按预期运行.如果你可以中止并中断finally块中的线程,这可能会对清理例程产生意想不到的后果,因此使应用程序处于损坏的状态 - 不好.
线程中断的细微差别在于,在线程进入finally块之前的任何时间都可能发出中断,但是当它没有处于某种SleepWaitJoin状态(即没有被阻塞)时.在这种情况下,如果finally块中存在阻塞调用,它将立即抛出ThreadInterruptedException并崩溃finally块.最后块阻止防止这种情况.
除了finally块中的保护之外,还扩展到try块和CERs(Constrained Execution Region),它们可以在用户代码中配置,以防止在执行区域之前抛出一系列异常 - 对于关键代码块非常有用这必须完成并延迟中止.
这个例外(没有双关语意思)就是所谓的Rude Aborts.这些是ThreadAbortExceptions由CLR托管环境本身引发的.这些可能导致最终和catch块退出,但不会导致CERs 退出.例如,CLR可能会引发Rude Aborts以响应其判断为执行其工作\退出的线程,例如在尝试卸载AppDomain或在SQL Server CLR中执行代码时.在您的特定示例中,当您的应用程序关闭并且AppDomain卸载时,CLR将在休眠线程上发出Rude Abort,因为存在AppDomain卸载超时.
在用户代码中不会发生中止和中断finally块,但两种情况之间的行为略有不同.
当调用Abortfinally块中的线程时,调用线程被阻塞.这是记录:
如果正在中止的线程位于受保护的代码区域(例如catch块,finally块或约束执行区域),则调用Abort的线程可能会阻塞.
在中止的情况下,如果睡眠不是无限的:
Abort在此处发出一个块,直到退出finally块,即它在此处停止,并且不会立即进入该Join语句. AbortRequested.AbortRequested它将继续执行finally块代码,然后"蒸发"即退出.Aborted.Join语句并在被调用线程退出时立即传递.因此,假设您的示例具有无限睡眠,则调用线程将在步骤1永久阻塞.
在中断的情况下,如果睡眠不是无限的:
记录不太好......
Interrupt并继续执行.Join语句上阻塞.ThreadInterruptedException在下一个阻塞调用上抛出一个(参见下面的代码示例).ThreadInterruptedException步骤6中未处理的线程现在已经平滑了该过程...所以再次给出你的无限睡眠的例子,调用线程将永远阻止,但在第2步.
因此,尽管Abort并Interrupt有略微不同的行为,他们都将导致被调用线程睡眠永远,并且调用线程阻塞永远(在你的例子).
只有粗鲁的中止可以强制阻塞的线程退出finally块,而这些只能由CLR本身引发(你甚至不能使用反射来执行,ThreadAbortException.ExceptionState因为它会进行内部CLR调用以获得AbortReason- 没有机会容易变得邪恶那里...).
CLR防止用户代码导致最终块被过早地退出以用于我们自己的好处 - 它有助于防止损坏的状态.
有关行为略有不同的示例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
}
}
Run Code Online (Sandbox Code Playgroud)
块的全部要点finally是保存不会受到中断或中止影响的东西,并且无论如何都会运行到正常完成。允许finally块中止或中断几乎就违背了这一点。遗憾的是,正如您所指出的,finally由于各种竞争条件,块可能会中止或中断。这就是为什么您会看到很多人建议您不要中断或中止线程。
相反,使用协作设计。如果线程应该被中断,Sleep请使用定时等待,而不是调用 。而不是调用Interrupt线程等待的信号。