How to release a lock and acquire another atomicly
In my application I manage a collection of locks that I need to serialize access to some of objects (each object is assigned a lock). This collection of locks (lock manager) also needs to be maintained in a thread-safe fashion (new locks are added/old locks are removed as new objects which require serialization are added/removed). The algorithm works something like this:
LockManager.Lock();
var myLock = LockManager.FindLock(myObject);
LockManager.Unlock(); // atomic
myLock.Lock(); // atomic
Swapping two lines is not a good solution. If locking of myLock
would block then this would also block unlocking of LockManager
making any requests for other locks to block.
Wh开发者_如何学Cat I would need is that the two marked lines are executed atomically. Is there a way to achieve this?
So you want to:
- guarantee that the individual lock (via
myLock
) was entered - then unlock the
LockManager
- make the above two operations atomic
- and not allow this new atomic operation to block if the individual lock cannot be entered immediately
Similar to how you cannot circumvent the laws of physics to create a perpetual motion machine you also cannot circumvent the laws of computation by executing a sequence of operations atomically in such manner that it does not block even if one of its constituents can, in fact, be expected to block. In other words, there is no way to make the operation complete until the individual parts also complete.
However, what we can do is attempt this atomic operation in an all-or-none manner that never blocks as long as we are okay with the "none" outcome. You see this a lot with the TryXXX
methods that exist on a lot of concurrent data structures. All you would need to do is define a TryLock
on your myLock
type. Then, the LockManager
could look like the following.
public class LockManager
{
public bool TryEnterIndividualLock(object value)
{
Lock();
try
{
var myLock = FindLock(value);
if (myLock != null)
{
return myLock.TryLock();
}
return false;
}
finally
{
Unlock();
}
}
}
Then the calling code would look like this:
while (!LockManager.TryEnterIndividualLock(myObject))
{
// Do something else until the lock can be entered.
}
This would give you the atomicity you were looking for, but at the cost of the operation not succeeding. If you are relying on this operation succeeding immediately then you are going to have to rethink your overall design.
精彩评论