代码之家  ›  专栏  ›  技术社区  ›  Todd Menier

只有在运行时才知道锁对象的理想范围时,如何锁定?

  •  1
  • Todd Menier  · 技术社区  · 6 年前

    这个要求相当简单:我想确保多个线程不会同时修改一个对象。棘手的部分是,对象来自一个工厂,其实现在运行时之前是未知的。它可以返回单个实例,可以每次创建一个新实例,也可以有一个共享实例池。

    var thing = factory.Get(...);
    lock (???) {
        // modify thing
    }
    

    我理解锁定其他代码可能锁定的公开可见对象是不安全的,因此可能导致死锁。换句话说,我不应该 lock (thing) . 但理想情况下我想锁定 某物 相同,仅在运行时范围内已知 thing .

    我提出的一个潜在解决方案是使用 ConcurrentDictionary 由键控的对象 事情 的哈希代码:

    private static ConcurrentDictionary<int, object> _thingLocks =
        new ConcurrentDictionary<int, object>();
    
    ...
    
    var thing = factory.Get(...);
    var thingLock = _thingLocks.GetOrAdd(thing.GetHashCode(), new object())
    
    lock (thingLock) {
        // modify thing
    }
    

    凭直觉,我认为这应该是可行的,因为a)锁本身是私有的,所以没有外部的东西可以锁定它们,b)锁实例是 极有可能 *1比1 事情 实例。但由于这类代码很难测试,我想问:这是正确和适当的解决方案吗?对于仅在运行时才知道的作用域,是否有更好/首选的锁定方法?

    *-正如评论中所指出的,这并不能保证,但在不太可能发生碰撞的情况下,这仅仅意味着2 事情 不能同时修改S,这是不理想的,但完全安全。

    1 回复  |  直到 6 年前
        1
  •  2
  •   Mike Zboray    6 年前

    ConditionalWeakTable

    class Thing
    {
        public int Value { get; set; }
    }
    
    abstract class ThingFactory
    {
        public abstract Thing GetOrCreate(string key);
    }
    
    class ThingManager
    {
        private readonly ConditionalWeakTable<Thing, object> _locks;
        private readonly ThingFactory _factory;
    
        public ThingManager(ThingFactory factory)
        {
            _factory = factory;
            _locks = new ConditionalWeakTable<Thing, object>();
        }
    
        public int Increment(string key,  int incr)
        {
            var thing = _factory.GetOrCreate(key);
            var thingLock = _locks.GetOrCreateValue(thing);
    
            lock (thingLock)
            {
                int newValue = thing.Value + incr;
                thing.Value = newValue;
                return newValue;
            }
        }
    }