5

I was wondering, If in a thread i have a lock statement and if that specific thread is closed while the lock is set, what happens with the lock ?

Are the other threads going to have access to the critical zone(does my specific lock variable get unlocked) or does the lock remain active and bricks my app ? If so, What solutions do i have to avoid the brick?

Alex
  • 10,869
  • 28
  • 93
  • 165
  • 2
    What do you mean by "that thread is closed"? Exits normally, throws an exception, call to .NET `Thread.Abort`, call to Win32 `TerminateThread`, or maybe something else? – Ben Voigt Nov 21 '10 at 19:25
  • Thread.Abort or throws an exception – Alex Nov 21 '10 at 19:31

4 Answers4

14

A lock statement:

lock (x)
{
    ...
}

is interpreted by the compiler in the resulting IL to:

Monitor.Enter(x);
try 
{
   ...
}
finally 
{
   Monitor.Exit(x);
}

So as you can see if an exception is thrown, the lock is released because of the finally statement. So even if you terminate the thread with Thread.Abort (which causes a ThreadAbortException to be thrown inside the thread) which you should ABSOLUTELY NEVER do, the lock will be released.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • 1
    Actually, that's no longer the real code generated, because it is not safe. The problem is that the `ThreadAbortException` can be raised during the call to `Monitor.Enter`, before or after the lock is acquired, and before the `try`/`finally` block scope. This is why using `Monitor.Enter(object)` is no longer used by the C# compiler since C# 4. See answers to https://stackoverflow.com/questions/2837070/lock-statement-vs-monitor-enter-method – Tim Lovell-Smith May 19 '21 at 15:32
3

A ThreadAbortException gets thrown which most of the time as Darin above has stated will result in the finally block being run which will release the lock.

But...

There are scenarios where this will not happen. The most common of which is if a NOP is inserted between the try & the finally by the compiler. If this occurs and the exception occurs at that particular point in time then the lock will become orphaned resulting in a deadlock scenario.

Błażej Michalik
  • 4,474
  • 40
  • 55
Johnv2020
  • 2,088
  • 3
  • 20
  • 36
  • @BadescuAlexandru NOP means no-op, or "no operation" which is an instruction that (usually) does nothing http://en.wikipedia.org/wiki/NOP – dss539 Aug 21 '12 at 16:30
  • This would only be an issue when compiling in Debug as the compiler generates the NOP for ease of debugging. Compiling in release does not generate NOP instructions. See: http://stackoverflow.com/a/13752156/1919294 – Yussuf Burke Mar 26 '14 at 17:52
1

You should never terminate(for example Thread.Abort or even worse raw winapi calls) a thread from the outside without unloading the app-domain afterwards, so this doesn't matter in practice. To terminate a thread set some kind of flag which the thread checks and then gracefully exits.

If you use Thread.Abort an asynchronous exception gets thrown, so the lock will be cleaned up if it's released in a finally clause(this is the case with the lock statement). But asynchronous exceptions easily corrupt state unless the code has been carefully with them in mind, so they should be avoided.

MSDN on ThreadAbortException:

When a call is made to the Abort method to destroy a thread, the common language runtime throws a ThreadAbortException. ThreadAbortException is a special exception that can be caught, but it will automatically be raised again at the end of the catch block. When this exception is raised, the runtime executes all the finally blocks before ending the thread. Because the thread can do an unbounded computation in the finally blocks or call Thread.ResetAbort to cancel the abort, there is no guarantee that the thread will ever end. If you want to wait until the aborted thread has ended, you can call the Thread.Join method. Join is a blocking call that does not return until the thread actually stops executing.

If you use raw winapi to abort a thread you're pretty much out of luck and should terminate the process as well.

CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
0

About the only reason to acquire a lock is so that you can change the state of an object and not have other threads see it in an invalid state. If something forcibly releases that lock before the state is restored to valid, the next thread to acquire the lock will blow up when it accesses the inconsistent state.

Think of, for example, the pointers in a doubly-linked list. What if the closed thread modified a forward pointer but not the corresponding back pointer? If you don't have code to cleanly close the thread, you are screwed whether the lock is automatically release or not. (If it is released, the next thread to acquire it will blow up when it accesses the broken pointers. If it not released, every thread to try to acquire it will hang.)

If you do have code to cleanly close the thread, surely it will also release the lock. So if this is your issue, you're doing something else wrong.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278