Jalal Said
Jalal Said

Reputation: 16162

Calling Monitor.TryEnter without Monitor.Exit at timer elapsed callback shows unexpected behavior

The question here is: If a thread that acquiring an exclusive lock to an object –for instance by using Monitor.Enter– is terminated, does that magically frees the exclusive lock on that object? if that is true, then suppose we call Monitor.Exit from another thread –because we assumed that object is locked–, isn't that will throw exeption of type SynchronizationLockException?

I am not sure it this is the expected behavior or a bug...

To demonstrate the issue, I have created a simple piece of code that calls Monitor.TryEnter without calling Monitor.Exit at System.Timers.Timer.Elapsed event callback "Note that the Timers.Timer class uses internally ThreadPool to run the callback event handler, and it may calls the elapsed callback each time on a different thread from the pool.."

class Foo
{
    private System.Timers.Timer _timer = new System.Timers.Timer(2000);//every 2s

    private static readonly object _locker = new object();//static locker object

    public Foo()
    {
        _timer.Elapsed += delegate
        {
            if (Monitor.TryEnter(_locker))//acquiring the lock without calling Exit.. 
            {
                Console.WriteLine(string.Format("Access Succeed!!, Thread Id {0}",
                    Thread.CurrentThread.ManagedThreadId));

                Thread.Sleep(6000);//simulate a work for 6 seconds
            }
            else
            {
                Console.WriteLine(string.Format("Unable to access to locker method within locker object, Thread Id {0}",
                    Thread.CurrentThread.ManagedThreadId));
            }
        };

        _timer.Start();
    }
}


//to test: just initialize a new instance of foo class
Foo foo = new Foo();
//blocks until application is terminated..
Thread.Sleep(Timeout.Infinite);

Results at output window shows:

Access Succeed!!, Thread Id 11
Unable to access to locker method within locker object, Thread Id 12
Unable to access to locker method within locker object, Thread Id 12
Access Succeed!!, Thread Id 11
Unable to access to locker method within locker object, Thread Id 12
Unable to access to locker method within locker object, Thread Id 12
Unable to access to locker method within locker object, Thread Id 12
Unable to access to locker method within locker object, Thread Id 12
Access Succeed!!, Thread Id 11
Unable to access to locker method within locker object, Thread Id 12
Unable to access to locker method within locker object, Thread Id 12
....

Upvotes: 1

Views: 1208

Answers (2)

Henk Holterman
Henk Holterman

Reputation: 273274

What you are seeing is that multiple calls to TryEnter from the same thread will succeed.

This is called re-entrancy and is usually used for recursive calls. You are using it a little strange, that's all.

If a thread that acquiring an exclusive lock to an object –for instance by using Monitor.Enter– is terminated, does that magically frees the exclusive lock on that object?

Alas, no.

That's why you should always combine TryEnter() with a try/finally to execute the Exit()

Upvotes: 2

SLaks
SLaks

Reputation: 887479

No threads are being terminated here.
Rather, they're returned to the threadpool, only to be reused by later timer events.

Monitor.Enter is re-entrant; entering a lock twice on the same thread will not block.

Upvotes: 2

Related Questions