避免递归的最佳C# ReaderWriterLockSlim实践

25

我有一个使用ReaderWriterLockSlim的类,其中包含一个读方法和一个写方法,写方法使用读方法来检索要修改的元素。快速示例:

class FooLocker
{
    ReaderWriterLockSlim locker = new ReaderWriterLockSlim();
    List<Foo> fooList = new List<Foo>();

    public void ChangeFoo(int index, string bar)
    {
        locker.EnterWriteLock();

        try
        {
            Foo foo = GetFoo(index);
            foo.Bar = bar;
        }
        finally
        {
            locker.ExitWriteLock();
        }
    }

    public Foo GetFoo(int index) 
    {
        locker.EnterReadLock(); //throws System.Threading.LockRecursionException

        try
        {
            return fooList[index];
        }
        finally
        {
            locker.ExitReadLock();
        }
    }

    //snipped code for adding instances etc.
}

如上所述,当调用ChangeFoo()时,由于在尝试进入读取锁定时已经持有写入锁定,因此此代码会抛出一个LockRecursionException

我已经检查了ReaderWriterLockSlim的文档,并且可以使用LockRecursionPolicy.SupportsRecursion来允许上述操作。然而,文档还建议在任何新开发中不要使用此方法,并且只应在升级现有代码时使用。

鉴于此,如何实现相同的结果才是最佳实践,其中写入方法可以使用只读方法来检索需要修改的内容?


你尝试过通过读取ReaderWriterLockSlim.IsReadLockHeld值来检查读锁是否被持有吗?http://msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim.isreadlockheld.aspx - Karel Frajták
设置SupportsRecursion在这里不是世界末日,但是Polity的答案是更好的方法。 - H H
1个回答

40

您可以将类分为暴露的方法和私有内部方法。内部方法执行例如获取逻辑,而公共方法执行锁定操作。例如:

class FooLocker 
{ 
    ReaderWriterLockSlim locker = new ReaderWriterLockSlim(); 
    List<Foo> fooList = new List<Foo>(); 


    public void ChangeFoo(int index, string bar) 
    { 
        locker.EnterWriteLock(); 

        try 
        { 
            Foo foo = UnsafeGetFoo(index); 
            foo.Bar = bar; 
        } 
        finally 
        { 
            locker.ExitWriteLock(); 
        } 
    } 

    public Foo GetFoo(int index)  
    { 
        locker.EnterReadLock();  

        try 
        { 
            return UnsafeGetFoo(index);
        } 
        finally 
        { 
            locker.ExitReadLock(); 
        } 
    } 

    private Foo UnsafeGetFoo(int index)
    {
        return fooList[index]; 
    }
} 

1
很棒的答案,谢谢!只需要确保私有的不安全方法有足够的文档说明,以确保没有人在未确保加锁的情况下使用它。 - Adam Rodger
4
@AdamRodger 文件并执行 System.Diagnostics.Debug.Assert(locker.IsReadLockHeld || locker.IsUpgradableReadLockHeld) - Kasper van den Berg
1
我不明白有什么区别,你只是将try中的代码移动到一个方法中,为什么这很重要? - shinzou
@shinzou 因为递归是通过锁定调用另一个锁定方法发生的。这将进入锁定的方法和需要(理想情况下,断言)已经进入的方法分开,从而避免了这种情况。 - Jon Hanna

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接