首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >应该在ReaderWriteLockSlim中使用双重检查锁定模式吗?

应该在ReaderWriteLockSlim中使用双重检查锁定模式吗?
EN

Stack Overflow用户
提问于 2011-08-04 19:33:22
回答 2查看 322关注 0票数 3

使用时需要使用ReaderWriterLockSlim双重检查锁模式吗?

考虑一下这个场景:我有一个dictionary。事情是可以加进去的。但事情是不能被移除的。当添加一些内容时,如果我想添加一些东西但它还没有添加,那么在时间上它可能是非常昂贵的操作(仅为几百毫秒,但相对于应用程序的其余部分而言仍然很昂贵),还会有什么收获吗:

  1. 首先获取一个读锁,然后检查是否存在,
  2. 然后输入可升级的读锁,然后再次检查
  3. 如果字典中还没有条目,那么输入写锁?

如下所示:

代码语言:javascript
运行
复制
void populateIfNotPresent( object thing )
{
        _lock.EnterReadLock( ) ;

        bool there = _dictionary.ContainsKey(thing);

        _lock.ExitReadLock( ) ;

        // Remember, the specs say nothing can be removed from this dictionary.
        if (!there)
        {
            _lock.EnterUpgradeableReadLock( ) ;

            try
            {
                if( !_dictionary.ContainsKey( thing ) )
                {
                    _lock.EnterWriteLock( ) ;
                    try
                    {
                        populate( thing ) ;
                    }
                    finally
                    {
                        _lock.ExitWriteLock( ) ;
                    }
                }
            }
            finally
            {
                _lock.ExitUpgradeableReadLock( ) ;
            }
        }
}

文档说,一次只有一个线程可以进入可升级的读锁,但不会阻止任何其他线程进入读锁,因此显示在双重检查锁中存在值

你认为如何?这是不是过火了?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2011-08-04 19:59:24

班级 (与任何其他读取器锁一样)意味着与写入的次数相比,读取次数较多。

实际上,您所做的是三次检查,这是多余的;您最好输入一个不可分级的写锁。如果项存在,则退出锁,否则,升级到写锁。

您的方法表明这里的读取没有提供任何值,因为您很有可能执行写入。由于可升级的写入不会阻止任何其他读取,所以它不应该在这里杀死您。

但是,如果这是您正在进行的读/写(或者大部分发生在这里)的唯一地方,那么存在一个问题,您的读/写比率不足以保证读/写锁,您应该使用其他的同步方法。

尽管如此,最后,这都是关于测试性能的;如果要优化一个实现,需要度量它的当前性能,以便与之进行比较,否则,它只是过早优化

票数 4
EN

Stack Overflow用户

发布于 2011-08-04 20:03:24

因此,在双重检查锁中似乎有值。

价值多少?好吧,如果您希望看到大量缓存丢失,那么执行可升级的锁可能是有意义的;但是,如果您是而不是,希望看到大量缓存错误,那么您正在执行不必要的锁定。一般说来,我会采用最简单的解决方案来完成任务。优化锁通常不是在哪里你会得到最大的冲击,你的钱,寻找更大的东西,首先优化。

建议:

一些东西可能会给你带来更大的冲击,你的钱是一个条形字典(Java的StripedMap是一个相当好的地方开始,它应该是很难理解)。

StripedMap/StripedDictionary的基本思想是有一个锁数组:

代码语言:javascript
运行
复制
object[] syncs = new object[n]();
// also create n new objects

您应该用足够多的条纹对映射进行条带化,以便允许您必须在不发生冲突的情况下输入该方法的线程数。我没有任何数据来支持这一点,但是假设您期望最多有8个线程进入映射,那么您可能需要使用8个或更多的锁(条纹)来确保所有8个线程都可以同时输入映射。如果你想要更好的针对“碰撞”的“保险”,那么创建更多的条纹,比如32条或64条。

当您输入populateIfNotPresent方法时,根据哈希代码锁定其中一个锁:

代码语言:javascript
运行
复制
void populateIfNotPresent( object thing )
{
    lock(syncs[thing.GetHashCode()%syncs.Length])
    {
        if(!dictionary.ContainsKey(thing))
        {
            populate(thing);
        }
    }
}

假设您有8个条纹,现在允许多达8个线程安全地进入并执行一个昂贵的操作,否则会阻塞其他7个线程。当然,假设哈希函数足够健壮,可以提供低复制概率的散列。

如果项目不存在,您已经希望populateIfNotPresent是昂贵的,但是如果您有一个带条的字典,那么您可以让多个线程在字典的不同扇区上工作,而不会相互碰撞。这将给您带来更大的好处,然后在检查对象是否存在时减少几个CPU周期,因为昂贵的操作是在对象确实存在时进行的。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6947499

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档