Why the flow does not stop when the sleeping end of the block

I look all over MSDN and can’t find the reason Thread cannot be interrupted when it was sleeping in the end block. I tried to abort without success.

Is there a way to wake the thread while sleeping in the end block?

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

Surprisingly, the MSDN application flow may be interrupted at the end of the block: http://msdn.microsoft.com/en-us/library/aa332364(v=vs.71).aspx "There is a possibility that the flow may be interrupted when the block finally is started, in which case the finally block will be interrupted. "

edit I find the Hans Passant comment to be the best answer, as this explains why Thread can sometimes and cannot be interrupted / interrupted at the end of a block. And this is when the process closes. thanks

+8
multithreading c #
source share
2 answers

Interruption and interruption of flows should be avoided, as this can lead to damage to the state of the running program. For example, imagine that you interrupted a thread that held locks on resources, these locks will never be released.

Instead, consider using a signaling mechanism so that threads can interact with each other and therefore correctly lock and unlock the descriptor, for example:

  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..."); } } 

Update

Pretty interesting low level problem. Although Abort() documented, Interrupt() much smaller.

The short answer to your question is no, you cannot wake a stream in a finally block by calling it Abort or Interrupt .

The inability to interrupt or interrupt flows in finally blocks by design, just so that ultimately the blocks can run as you expected. If you could interrupt and interrupt threads in finally blocks, this could lead to unforeseen consequences for cleaning procedures, and therefore leaving the application in a damaged state is not good.

A small nuance with thread interruption is that the interrupt may have been thrown against the thread at any time before it entered the finally block, but so far it has not been in the SleepWaitJoin state (i.e. not blocked). In this case, if there was a blocking call in the finally block, it immediately threw a ThreadInterruptedException and broke out of the finally block. Finally, block protection prevents this.

In addition to protection in finally blocks, this extends to try blocks, as well as to CER ( Constrained Execution Region ), which can be configured in user code to prevent the exclusion of a number of exceptions until a region is executed - very useful for critical code blocks to be completed, and delay interruptions.

An exception (no pun intended ) is what is called Rude Aborts . These are ThreadAbortExceptions created by the CLR hosting environment. This can lead to the fact that the final and blocking blocks will be displayed, but not CERs. For example, the CLR may call Rude Aborts in response to threads that, in its opinion, are too long to complete their work / exit, for example. while trying to unload AppDomain or execute code in SQL Server CLR. In your specific example, when your application shuts down and the AppDomain is unloaded, the CLR will issue Rude Abort in a sleep thread, as there will be a waiting time for the AppDomain to unload.

Undoing and interrupting in finally blocks will not happen in the user code, but there is a slightly different behavior between the two cases.

Abort

When Abort called in a thread in a finally block, the calling thread is blocked. This is documented :

The thread that calls Abort can block if the thread that is interrupted is in a protected area of ​​code, such as a catch block, finally block, or a limited execution area.

In case of interruption, if the dream was not endless:

  • The calling thread throws Abort , but blocks it until the finally block is completed, i.e. it stops here and does not immediately jump to the Join statement.
  • The called party stream is set to AbortRequested .
  • The caller continues to sleep.
  • When the called call wakes up, since it has the AbortRequested state, it will continue to execute the code of the finally block and then "evaporate", that is, it will exit.
  • When the interrupted thread leaves the finally block: an exception does not occur, after the completion of the finally block, the code is absent, and the state of the Aborted thread.
  • The calling thread unlocks, continues the Join statement, and immediately jumps to how the called thread is called.

Therefore, given your endless sleep example, the calling thread will block forever in step 1.

Interrupt

In case of interruption, if the dream was not endless:

Not so well documented ...

  • The calling thread will issue Interrupt and continue execution.
  • The calling thread will block the Join statement.
  • The callee thread has its own status set to raise an exception on the next blocking call, but, anyway, it is in the finally block, it is not unlocked, that is, woken.
  • The caller continues to sleep.
  • When waking up, it will continue to execute the finally block.
  • When an interrupted thread leaves the finally block, it throws a ThreadInterruptedException in its next blocking call (see the code example below).
  • The calling thread "joins" and continues as it ThreadInterruptedException called thread, however, an unhandled ThreadInterruptedException in step 6 has now smoothed the process ...

So, again, your example with endless sleep, the calling thread will be blocked forever, but in step 2.

Summary

So, although Abort and Interrupt have a slightly different behavior, they will cause the called thread to sleep forever and the calling thread to block forever (in your example).

Only Rude Abort can make a blocked thread exit the finally block, and they can only be raised by the CLR itself (you cannot even use reflection for diddle ThreadAbortException.ExceptionState , since it makes an internal CLR call to get AbortReason - there is no way to be easily evil there. ..).

The CLR prevents the premature failure of code blocks for our own good - it helps prevent a damaged state.

An example of a slightly different behavior with 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 } } 
+8
source share

The entire part of the finally block must contain something that will not be affected by interruption or interruption, and will be executed until normal completion no matter what. Allowing the finally block to be aborted or aborted will pretty much defeat the point. Unfortunately, as you noted, finally blocks can be interrupted or interrupted due to different race conditions. This is why you will see that many people advise you not to interrupt or interrupt flows.

Instead, use a collaborative design. If the thread should be interrupted, instead of calling Sleep use a temporary wait. Instead of calling Interrupt signal what the stream expects.

+4
source share

All Articles