代码之家  ›  专栏  ›  技术社区  ›  Feryt

C#:使用共享资源的最佳方式?

  •  0
  • Feryt  · 技术社区  · 16 年前

    我需要序列化对共享资源(即缓存)的访问。我使用后面描述的模式,但有时,特别是前两个线程,会加载两次数据。问题在哪里?

    public class Entity { }
    
    public class CacheManager
    {
        public static CacheManager Instance = new CacheManager();
    
        private CacheManager()
        {
            _thisLock = new Object();
            _cache = new Dictionary<int, Entity>();
        }
    
        private readonly Object _thisLock;
        private readonly IDictionary<int, Entity> _cache;
    
        public Entity GetEntity(int id)
        {
            Entity entity;
            if ( !_cache.TryGetValue(id, out entity) ) {
                /* Only one thread, at a time, can go inside lock statement.
                 * So, if threads [1] and [2] came here at the same time, .NET shoud pass thread [1] inside and [2] waits. */
                lock ( _thisLock ) {
                    if ( !_cache.TryGetValue(id, out entity) ) { /* If we are [2] : check(and loads from cache) if [1] did work for us. */
                        /* we are [1] so let's load entity from repository and add it to cache */
                        entity = new Entity(); // simulate repository access
                        _cache.Add(id, entity);
                    }
                }
            }
            return entity;
        }
    }
    

    为什么两个线程都在lock语句中执行步骤?

    非常感谢。

    3 回复  |  直到 16 年前
        1
  •  3
  •   Yann Schwartz    16 年前

    您的代码不是线程安全的。由于您没有锁定第一个TryGetValue,因此线程可能会在修改字典(使用Add)时尝试访问字典。在当前的.Net字典实现中,如果正在修改基础字典,TryGetValue可能会引发异常。

    您最好完全转储外部TryGetValue,或者使用ReaderWriterLockSlim。

    要回答您的问题,可能是因为您没有从调用代码访问您的单例(实例)?无论如何,不要使用这种锁定策略,它很脆弱。使用ReaderWriterLockSlim或转储第一个TryGetValue。

        2
  •  1
  •   TheEruditeTroglodyte    16 年前

    这里有一个可怕的想法:我只在调试模式下为lock()生成的代码中遇到了公认的MS bug 3.5sp1 CLR(如果您感兴趣,我将尝试查找)。我发现在调试会话期间,我将有两个线程在lock()语句中执行代码——这不太好。您可以通过在lock()中使用包含 interlocked 在int上递增/递减。如果该联锁变量超过1,我将调用debugger.break()。您还可以创建另一个变量,该变量在锁定之前递增,然后在锁定内部立即递减(再次使用联锁增量)。这些变量将显示等待锁的线程数以及lock()语句本身中当前执行代码的线程数 祝你好运

        3
  •  0
  •   Adriaan Stander    16 年前

    我建议添加锁 之前 if语句。