2

在系统中,我们有通过特定参数锁定对象的方法。作为实现,我们有 LockManager 和 Enter 方法,它接收一个锁的键,检查锁对象是否存在于内部字典中,如果不存在,它创建它然后锁定。

我想要做的是为特定锁定设置“X 预期时间”,如果一个对象被锁定超过 X 时间,我想在我们的日志中写一条消息。

下面是我的锁管理器的源代码:

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]);
        }
    }
}

现在我想添加一个额外的方法,比如说 LockTimoutHandler(T key),如果特定键的对象被锁定超过 X 时间,它将被调用。

为了做到这一点,我想为“Enter”和“Exit”方法添加一些逻辑。当 Enter 被调用时,某些东西会以某种方式注册以在 X 时间内运行 LockTimoutHandler,而当“Exit”被调用时,某些东西会以某种方式被 unregistered

我的问题是我可以用什么来代替那个东西?我如何安排该方法在 X 时间运行以及之前是否发生过退出,因此请删除该计划。它必须非常快,因为在我们的案例中性能非常重要。我知道 Timer 对象...它可以延迟执行方法,但它的性能是否足够好?我必须有哪些额外的选择才能实现这一目标?

注意:为了清楚起见,我不是在谈论TryEnter。当对象无法锁定特定时间量时,我不想捕获这种情况,我想捕获已经锁定时间过长的对象。

谢谢!

4

2 回答 2

2

我们有类似的要求并以这种方式解决了它:

  • 锁定时,将计时器设置为超时,向其传递一个状态对象,其中包含您想要的任何内容的密钥和委托:记录、强制解锁……您的用例所需的任何内容
  • 当计时器触发时,检查密钥,如果条目存在调用委托
  • 重要提示:回收计时器(线程安全队列中的 eq),不要让它超出范围。
  • 当您下次需要一个计时器时,从回收队列中取出一个并操作状态对象 - 仅在需要时创建一个新的。

这将根据需要保留尽可能多的计时器,但不会更多,并且只会产生一次分配/解除分配的成本。由于您无法更改计时器的状态对象,因此您需要更改其内容。

于 2012-12-25T12:15:23.103 回答
1

我认为你可以做得更简单。计时器是重量很轻的物体,我不会试图限制它们的数量。所有计时器都在线程池的一个特殊线程中运行,它们非常便宜。

只需创建一个计时器字典(如果它将在不同的线程中使用,您可能需要将其更改为ConcurrentDictionary

var timers = new Dictionary<T, Timer>();

将项目添加到列表时,使用此代码设置超时:

var timer = new Timer(o => LogMessage("key {0} is being held too long", key));
timer.Change(timeout, Timeout.Infinite);
timers.Add(key, timer);

请注意,计时器只会在指定的超时后执行一次。当项目被释放时,只需从字典中删除计时器:

Timer timer;
if (timers.TryGetValue(key, out timer))
{
    timer.Dispose();
    timers.Remove(key);
}
于 2012-12-25T13:09:09.570 回答