问题
In system we have methods that lock an object by specific params. As implementation, we have LockManager with Enter method that receives a key for a lock, check if lock object exists in internal dictionary, if not, it creates it and then locks.
What I want to do, is to set "X expected time" for specific lock, and if an object was locked for more that X time, I want to write a message to our log.
Below is a source code for my lock manager:
public class MyLockManager<T>
{
protected Dictionary<T, object> LockDictionary { get; private set; }
public MyLockManager()
{
LockDictionary = new Dictionary<T, object>();
}
/// <summary>
/// Enters a lock on the key.
/// </summary>
/// <param name="key">The key to lock.</param>
public virtual void Enter(T key)
{
if (!LockDictionary.ContainsKey(key))
{
lock (LockDictionary)
{
if (!LockDictionary.ContainsKey(key))
{
LockDictionary.Add(key, new object());
}
}
}
object lockObj = LockDictionary[key];
Monitor.Enter(lockObj);
}
/// <summary>
/// Releases the lock on the key.
/// </summary>
/// <param name="key">The key to release.</param>
public virtual void Exit(T key)
{
if (LockDictionary.ContainsKey(key))
{
Monitor.Exit(LockDictionary[key]);
}
}
}
Now I want to add an additional method, lets say LockTimoutHandler(T key) that will be called, if object for specific key was locked for more than X time.
In order to do it, I want to add some logic to "Enter" and "Exit" methods. When Enter called, something will be somehow registered to run the LockTimoutHandler in X time, and when "Exit" called, that something will be somehow unregistered.
My question is what I can use instead of that something? How I can schedule the method to run in X time and if Exit occurred before, so remove the schedule. It must be very fast, since performance is very important in our case. I know about Timer object...it can execute method in delayed manner, but is it's performance good enough? What additional options I have to achieve that?
NOTE: Just to be clear, I am not talking about TryEnter. I am not trying to catch the case when the object can not be locked for specific amount of time, I want to catch objects that already locked for too much time.
Thanks!
回答1:
We had a similar rquirement and solved it this way:
- When locking, set a timer to the timeout passing it a state object, that contains the key and a delegate whatever you desire: Logging, force-unlock, ... whatever is needed for your use case
- When the timer fires, check for the key, if entry exists call delegate
- Important: recycle the timer (e.q. in a threadsafe queue), do not let it go out of scope.
- When you next need a timer, take one out of the recycle queue and manipulate the state object - only create a new one if you need to.
This will keep as many timers around as necessary, but no more and will incur the cost of allocation/deallocation only once. Since you can't change the timer's state object, you need to change its contents.
回答2:
I think you can do it simpler. Timer is very light weight object and I wouldn't bare with trying to limit their count. All timers are running in a special thread from threadpool and they are really cheap.
Just create a dictionary of timers (if it will be used from different threads you may want to change it to ConcurrentDictionary
:
var timers = new Dictionary<T, Timer>();
When adding item to your list use this code to setup timeout:
var timer = new Timer(o => LogMessage("key {0} is being held too long", key));
timer.Change(timeout, Timeout.Infinite);
timers.Add(key, timer);
Note that timer will be executed only once -- after specified timeout. And when item is released just remove timer from the dictionary:
Timer timer;
if (timers.TryGetValue(key, out timer))
{
timer.Dispose();
timers.Remove(key);
}
来源:https://stackoverflow.com/questions/14030491/net-multithreading-lock-object-with-log-when-locked-for-to-much-time