Question on using Monitor.TryEnter and locking object
Consider the following function that implements non-blocking access to only the one thread.
public bool TryCancelGroup()
{
if (Monitor.TryEnter(_locked))
{
if (_locked == false)
{
_locked = true;
try
{
// do something
}
catch (Exception ex)
{
_locked = false;
}
finally
{
Monitor.Exit(_locked);
}
}
return _locked;
}
else
{
return false;
}
}
And here is how _locked
variable is defined.
bool _locked = false;
Now when program reaches Monitor.Exit(_locked);
it throws an System.Threading.SynchronizationLockException
saying that _locked varia开发者_运维问答ble was not synchronized before.
It all was working before when _locked variable was defined as object
object _locked = new object();
When I changed it to bool in order to use it as boolean flag I started getting this exception.
The reason why is that the Monitor
methods all take a System.Object
parameter. When you pass in a bool
a box is required to convert to Object
. The box operation produces a new System.Object
value for each call. So the TryEnter
and Exit
methods see different objects and results in the exception.
When _locked
was typed to Object
there was no need for a box. Hence the TryEnter
and Exit
methods see the same object and can function correctly.
A few other comments about the code
- TryEnter must be paired with Exit in all cases and for sanity sake the Exit call should be in a finally block. Otherwise you're inviting a deadlock scenario
- The
_locked
variable is only set tofalse
in the face of an exception. If execution does not produce an exception it will remain true and no thread will ever again enter theif
block.
Setting the timeout on a monitor to 0 can help implement the behaviour you want. Use a globally declared object to lock against.
static object mylock = new object();
....
if (Monitor.TryEnter(mylock, 0))
{
try
{
// Do work
}
finally
{
Monitor.Exit(mylock);
}
}
To add to the success above - to be sure the lock is released - the TryEnter() and Exit() can be wrapped in a custom class as an extension to object taking a Delegate and Timeout as a parameter.
public static class MyMonitor
{
public static bool TryEnter(this object obj, Action action, int millisecondsTimeout)
{
if (Monitor.TryEnter(obj, millisecondsTimeout))
{
try
{
action();
}
finally
{
Monitor.Exit(obj);
}
return true;
}
else
{
return false;
}
}
}
And called like this waiting 1000 ms to obtain the lock or throw an error if timeout:
if (!_locked.TryEnter(() =>
{
//Exclusive access code placed here..
}, 1000)) {
throw new TimeoutException("Timeout waiting for exclusive access");
}
This way the forgetting the Monitor.Exit() is not an option.
精彩评论