patterncsharpMinor
Exclusive access to objects
Viewed 0 times
exclusiveaccessobjects
Problem
Take a look at following code:
The idea is to inherit from
Expected scenarios of usage:
Both cases are covered with something like:
The questions:
-
Is this a good approach to get exclusive access? Code-wise and architecturally? E.g. I can use
-
Is there a possibility to avoid
/*abstract*/ class Capturable
{
object _lock = new object();
public bool IsCaptured { get; private set; }
public bool TryCapture()
{
if (!IsCaptured)
lock (_lock)
if (!IsCaptured)
return IsCaptured = true;
return false;
}
public void Release()
{
lock (_lock)
IsCaptured = false;
}
}The idea is to inherit from
Capturable when I need an object with exclusive access. Object will be used from different places (not necessarily different threads). If this object is needed, then the caller should first call TryCapture() (so theoretically there could be many calls when object is occupied, therefore double check locking is used to quickly return false).Expected scenarios of usage:
- periodically do something (e.g. by timer);
- get exclusive access for a duration (e.g. if there is a long operation during which nobody should change state of object);
Both cases are covered with something like:
var obj = Manager.Get();
...
if(obj.TryCapture())
try
{
...
obj.SomeMethod();
...
}
finally
{
obj.Release();
}The questions:
-
Is this a good approach to get exclusive access? Code-wise and architecturally? E.g. I can use
Monitor.TryEnter instead of double check locking, but not sure how to return IsCaptured then.-
Is there a possibility to avoid
if+TryCapture+try+finally everywhere? Similar to how clean lock looks like (it covers Monitor calls) or how nice using is to ensure Dispose() call. The problem here comes when multiple of such object has to be captured at the beginning of method, then it will looks like dozens of nested if/try/finally, which I don't like.Solution
What happens if this is called by something that hasn't captured the object?
Honestly, I don't see what this is giving you that you don't get from:
and
Unless you want to release the capture from a different thread, in which case it seems like you're more likely to run into the 'Released by wrong thing` problem.
Why do you need to know if it's captured? Try to capture the lock and if it fails, you know that it's captured... What purpose does the property really serve?
The problem here comes when multiple of such object has to be captured at the beginning of method, then it will looks like dozens of nested if/try/finally
If you're going to be capturing groups of objects then you're going to need to be very careful about the order that you capture those objects in order to prevent deadlocks where one method performs:
And another method performs
One of the things you've indicated is that you don't like the idea of the nested try/finally blocks to ensure that all captured items are released. You could consider writing some utility functions to help make this cleaner. Something like this:
Would allow you to lock and release groups of objects like this:
public void Release()
{
lock (_lock)
IsCaptured = false;
}Honestly, I don't see what this is giving you that you don't get from:
Monitor.TryEnter(lockObj)and
Monitor.Exit(lockObj);Unless you want to release the capture from a different thread, in which case it seems like you're more likely to run into the 'Released by wrong thing` problem.
Why do you need to know if it's captured? Try to capture the lock and if it fails, you know that it's captured... What purpose does the property really serve?
The problem here comes when multiple of such object has to be captured at the beginning of method, then it will looks like dozens of nested if/try/finally
If you're going to be capturing groups of objects then you're going to need to be very careful about the order that you capture those objects in order to prevent deadlocks where one method performs:
Capture A, B, CAnd another method performs
Capture C, AOne of the things you've indicated is that you don't like the idea of the nested try/finally blocks to ensure that all captured items are released. You could consider writing some utility functions to help make this cleaner. Something like this:
static bool TryCaptureAll(params Capturable[] capturables)
{
List capturedItems = new List();
foreach(var capturable in capturables)
{
if(!capturable.TryCapture())
{
// Failed to capture one of the items, release the ones
// we did capture and return failure.
foreach(var captured in capturedItems)
{
captured.Release();
}
return false;
}
capturedItems.Add(capturable);
}
return true;
}
static void ReleaseAll(params Capturable[] capturables)
{
foreach (var capturable in capturables)
{
capturable.Release();
}
}Would allow you to lock and release groups of objects like this:
if (CaptureList.TryCaptureAll(a, b, c))
{
try
{
a.SomeMethod();
b.SomeMethod();
c.SomeMethod();
}
finally
{
CaptureList.ReleaseAll(a, b, c);
}
}Code Snippets
public void Release()
{
lock (_lock)
IsCaptured = false;
}Monitor.TryEnter(lockObj)Monitor.Exit(lockObj);Capture A, B, CCapture C, AContext
StackExchange Code Review Q#132179, answer score: 6
Revisions (0)
No revisions yet.