.NET多线程:锁定对象时锁定对象很长时间

时间:2012-12-25 12:03:58

标签: c# .net multithreading locking

在系统中,我们有通过特定参数锁定对象的方法。 作为实现,我们使用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键),如果特定键的对象被锁定超过X时间。

为了做到这一点,我想为“Enter”和“Exit”方法添加一些逻辑。当Enter调用时, 会以某种方式注册 以在X时间内运行LockTimoutHandler,并且当调用“Exit”时, 某些内容将以某种方式取消注册

我的问题是我可以使用什么而不是 某些 ?我如何安排方法在X时间运行,如果之前发生退出,那么删除计划。它必须非常快,因为在我们的情况下性能非常重要。我知道Timer对象......它可以延迟执行方法,但它的性能是否足够好?我还有哪些其他选择?

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

谢谢!

2 个答案:

答案 0 :(得分:2)

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

  • 当锁定时,将计时器设置为超时,向其传递一个状态对象,其中包含密钥和委托,无论您需要什么:记录,强制解锁,...您的用例需要的任何内容
  • 当计时器触发时,检查密钥,如果条目存在则调用委托
  • 重要事项:回收计时器(e.q.在线程安全队列中),让它超出范围。
  • 当您下次需要一个计时器时,从循环队列中取出一个并操纵状态对象 - 如果需要,只创建一个新对象。

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

答案 1 :(得分: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);
}