【问题标题】:Lock dictionary keeps growing, indeas on how to clean up?锁字典不断增长,不知道如何清理?
【发布时间】:2011-04-28 14:49:02
【问题描述】:

我有一个基于网络的商品订购系统。

  • 商品的时间非常有限,仅售 Y 小时
  • 每件商品只允许 X 次订单

为了保持每件商品的订单

private static Dictionary<Guid, Object> PurchaseLockDictionary = null;

private static object GetLock(Guid itemId)
    {
        if (!PurchaseLockDictionary.ContainsKey(itemId))
        {
            PurchaseLockDictionary.Add(itemId, new object());
        }
        return PurchaseLockDictionary[itemId];
    }

购买看起来像这样:

public static Order Purchase(Buyer buyer, OrderItem item)
    {
        Order order;
        try
        {
            lock (GetLock(item.Id))
            {
                // order stuff like counting current amount of orders, buyer validity etc
            }
        } catch (Exception e) {
            // Exception stuff
        }
        return order;
    }

现在,我的问题是,如何防止我的锁定机制(Dictionary 对象)不按比例增长?目前我们出于其他原因每周重新启动服务器,但我不希望代码依赖这种行为。

还有其他更适合这种锁定机制的数据结构吗?或者有没有一种智能的方法来查找和清理字典中的旧条目?非常欢迎提出想法!

【问题讨论】:

  • 如果这段代码是从多个线程调用的(如果不是,则根本不需要任何锁),那么您需要围绕字典本身设置某种大的全局锁。如果没有,那么当多个线程点击ContainsKeyAdd、索引器等时,您将面临竞争条件的风险,甚至损坏您的字典。
  • 听起来您想使用缓存解决方案,该解决方案将使不经常使用/访问的项目过期。如果是这样,那么您应该看看 System.Runtime.Caching msdn.microsoft.com/en-us/library/system.runtime.caching.aspx
  • 您是重复使用您添加到字典中的对象来计算订购商品的数量还是仅将其用作锁?
  • @LukeH :多线程,是的,基于网络。 @fantasticfix:只是为了加锁,在锁内进行计数。

标签: c# data-structures locking memory-management race-condition


【解决方案1】:
using (var locker = new PurchaseLocker(item.Id))
{
    // order stuff like counting current amount of orders, buyer validity etc
}

// ...

public sealed class PurchaseLocker : IDisposable
{
    private static readonly object _bigLock = new object();
    private static readonly Dictionary<Guid, LockToken> _lockMap = new Dictionary<Guid, LockToken>();
    private readonly Guid _itemId;

    public PurchaseLocker(Guid itemId)
    {
        _itemId = itemId;

        LockToken miniLock;
        lock (_bigLock)
        {
            if (!_lockMap.TryGetValue(itemId, out miniLock))
            {
                miniLock = new LockToken();
                _lockMap.Add(itemId, miniLock);
            }
            miniLock.Count++;
        }
        Monitor.Enter(miniLock);
    }

    public void Dispose()
    {
        lock (_bigLock)
        {
            LockToken miniLock = _lockMap[_itemId];
            miniLock.Count--;
            if (miniLock.Count == 0)
                _lockMap.Remove(_itemId);

            Monitor.Exit(miniLock);
        }
    }

    private sealed class LockToken
    {
        public int Count;
    }
}

【讨论】:

  • 这实际上看起来是一个完美的解决方案。使用起来真的很漂亮!我怎么没想到!不影响当前代码。实际上可以直接复制粘贴到我的代码中并开始测试!
【解决方案2】:

听起来您想使用缓存解决方案,该解决方案将使不经常使用/访问的项目过期。如果是这样,那么您应该查看System.Runtime.Caching。您可以将项目添加到缓存中并设置其到期政策等。

【讨论】:

    【解决方案3】:

    如果您在多线程程序中使用它,您将遇到麻烦。 Dictionary 不是线程安全的。考虑改用ConcurrentDictionary

    对于您的商品,我假设您每次有人订购时都会增加订单计数。当商品的订单数量达到最大值或商品变得特殊时,您不能让Purchase 方法从字典中删除该商品吗?

    【讨论】:

    • 感谢您提供指向 ConcurrentDictionary 的指针!是的,我可以在购买达到 X 时删除条目,但在最坏的情况下,每件商品被购买 X-1 次,并且没有条目被删除。
    • @mofoo:但你说项目是有时间限制的。因此,一旦项目的时间到期(即该项目不再销售),您应该能够从字典中删除该项目。
    • 是的,我可以有这样的解决方案.. 但是我需要检查过期项目,我什么时候进行检查?它会起作用,但它会在订单事务中产生更多开销,而不是那些漂亮的代码。
    • @mofoo:我想你已经有一些方法可以防止不再可用的东西被订购,因此可以调用删除方法。
    猜你喜欢
    • 2013-09-03
    • 2021-06-24
    • 2010-09-14
    • 2016-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-03
    相关资源
    最近更新 更多