C#带ReaderWriterLockSlim的字典

C#带ReaderWriterLockSlim的字典,c#,asp.net,caching,dictionary,thread-safety,C#,Asp.net,Caching,Dictionary,Thread Safety,我对多线程非常陌生,出于某种原因,这门课给我带来了更多的麻烦 我正在ASP.net缓存中设置一个字典-它将经常查询单个对象,偶尔枚举,并且极不频繁地编写。我要注意的是,字典数据几乎从未更改过,我计划每天让它过期,并在它离开缓存时通过回调从数据库重建 我相信,只要没有编写字典,通过键进行枚举和访问是安全的。我认为一个基于ReaderWriterLockSlim的包装类是一个不错的选择,但我有几点不太清楚 如果我使用Lock,我相信我可以锁定令牌或我正在保护的实际对象。我不知道如何使用ReaderW

我对多线程非常陌生,出于某种原因,这门课给我带来了更多的麻烦

我正在ASP.net缓存中设置一个字典-它将经常查询单个对象,偶尔枚举,并且极不频繁地编写。我要注意的是,字典数据几乎从未更改过,我计划每天让它过期,并在它离开缓存时通过回调从数据库重建

我相信,只要没有编写字典,通过键进行枚举和访问是安全的。我认为一个基于ReaderWriterLockSlim的包装类是一个不错的选择,但我有几点不太清楚

如果我使用Lock,我相信我可以锁定令牌或我正在保护的实际对象。我不知道如何使用ReaderWriter锁来做类似的事情。我认为我的包装器的多个实例不会正确锁定,因为readerwriterlock不在彼此的范围内,这是正确的吗

编写这样的包装器的最佳实践是什么?将其构建为静态对象似乎是多余的,因为主要对象由缓存维护。Singleton’s似乎不受欢迎,我关心的是上述针对个别实例的范围界定问题

我已经看到了一些类似包装器的实现,但我无法回答这些问题。我只是想确保我对自己正在做的事情有一个坚定的把握,而不是一路剪切粘贴。非常感谢你的帮助


**编辑:希望这是我试图找出的一个更清晰的摘要-**

1。我认为锁不会影响基础数据,并且与任何其他变量的作用域相同,对吗?

举个例子,假设我有以下几点-

MyWrapperClass 
{
    ReaderWriterLockSlim lck = new ReaderWriterLockSlim();
    Do stuff with this lock on the underlying cached dictionary object...
}

MyWrapperClass wrapA = new MyWrapperClass();
MyWrapperClass wrapB = new MyWrapperClass();
我认为wrapA锁和wrapB锁不会相互作用,并且如果wrapA和wrapB都尝试操作,这将是不安全的,这是对的吗

2。如果是这种情况,“共享”锁数据的最佳做法是什么?


这是一个Asp.net应用程序-将有多个页面需要访问数据,这就是为什么我在第一时间这样做。确保不同包装器使用相同锁的最佳实践是什么?我的包装器应该是所有线程都在使用的静态包装器还是单例包装器,如果不是的话,更优雅的替代方案是什么

锁定的标准方式是:
object lck=new object()。。。lock(lck){…}
在本例中,对象lck表示锁

ReadWriterLockSlim没有太大的不同,只是在本例中,实际的ReadWriterLockSlim类代表实际的锁,因此无论您在哪里使用lck,现在都可以使用ReadWriterLockSlim

ReadWriterLockSlim lck = new ReadWriterLockSlim();

...

lck.EnterReadLock();
try
{
    ...
}
finally
{
    lck.ExitReadLock();
}

缓存中有多个dictionary对象,并且希望每个对象单独锁定。“最好”的方法是只使用一个简单的类为您完成它

public class ReadWriteDictionary<K,V>
{
    private readonly Dictionary<K,V> dict = new Dictionary<K,V>();
    private readonly ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();

    public V Get(K key)
    {
        return ReadLock(() => dict[key]);
    }

    public void Set(K key, V value)
    {
        WriteLock(() => dict.Add(key, value));
    }

    public IEnumerable<KeyValuePair<K, V>> GetPairs()
    {
        return ReadLock(() => dict.ToList());
    }

    private V2 ReadLock<V2>(Func<V2> func)
    {
        rwLock.EnterReadLock();
        try
        {
            return func();
        }
        finally
        {
            rwLock.ExitReadLock();
        }
    }

    private void WriteLock(Action action)
    {
        rwLock.EnterWriteLock();
        try
        {
            action();
        }
        finally
        {
            rwLock.ExitWriteLock();
        }
    }
}

Cache["somekey"] = new ReadWriteDictionary<string,int>();
公共类读写目录
{
专用只读词典dict=新词典();
private ReaderWriterLockSlim rBlock=new ReaderWriterLockSlim();
公共V Get(K键)
{
返回ReadLock(()=>dict[key]);
}
公共无效集(K键,V值)
{
写锁(()=>dict.Add(键,值));
}
公共IEnumerable GetPairs()
{
返回ReadLock(()=>dict.ToList());
}
专用V2读锁(Func Func)
{
rwLock.EnterReadLock();
尝试
{
返回func();
}
最后
{
rwLock.exitradlock();
}
}
私有无效写回(操作)
{
rBlock.EnterWriteLock();
尝试
{
动作();
}
最后
{
rBlock.ExitWriteLock();
}
}
}
缓存[“somekey”]=新的读写指令();
的帮助页上还有一个更完整的示例。让它变得通用并不难

编辑以回答您的新问题-

1.)您是对的,wrapA和wrapB不会交互。它们都有自己的ReaderWriterLockSlim实例


2.)如果您需要所有包装器类之间的共享锁,那么它必须是静态的。

执行您想要的所有操作,然后执行一些操作。System.Concurrent.Collections的一部分

这就是我现在设置锁的方式,但是如果我的包装器对象有多个实例,我担心lck对象将不在同一范围内,因此不会按预期进行交互。当然,我可以将lck设置为静态变量,或者将整个包装器类设置为静态,但我想看看是否有必要这样做,是否正确。谢谢这两者都不是一个好的计划,请确保您只有一个这样的包装器类。事实上,不要把它看作包装类,而要把它看作实际的类,因为它使用字典是该类的一个实现细节。在这一点上,你不应该在所说的课外透露字典。我只是在你发帖时修改了这个问题。这实际上完全回避了我所关心的问题。我想我应该在内部缓存字典,而包装器应该是外部的,但是你的解决方案看起来确实更优雅。谢谢太好了-非常感谢!缓存整个对象而不仅仅是字典似乎是一种更好的方法。我真的很感谢你的澄清,我害怕在不确定自己在做什么的情况下继续前进,我似乎找不到一个可靠的例子来证实这一点。很抱歉在你发帖时更改了你的问题!我再次感谢所有的帮助,使事情更加清楚!警告即使阅读字典,枚举字典也不是线程安全的。GetPairs()应该有写锁,而不是读锁。因此,上述代码可能会失败,因为它是现在谢谢!不幸的是,在我之前调查这件事时,它并不存在。很高兴现在拥有它