微软对ReaderWriterLockSlim.IsReadLockHeld / IsWriteLockHeld及其后果的评论

时间:2013-06-11 14:50:08

标签: c# readerwriterlockslim

要同步对我的属性的访问,我使用ReaderWriterLockSlim类。我使用以下代码以线程安全的方式访问我的属性。

public class SomeClass
{
    public readonly ReaderWriterLockSlim SyncObj = new ReaderWriterLockSlim();  
    public string AProperty
    {
        get
        {
            if (SyncObj.IsReadLockHeld)
                return ComplexGetterMethod();
            SyncObj.EnterReadLock();
            try
            {
                return ComplexGetterMethod();
            }
            finally
            {
                SyncObj.ExitReadLock();
            }
        }
        set
        {
            if (SyncObj.IsWriteLockHeld)
                ComplexSetterMethod(value);
            else
            {
                SyncObj.EnterWriteLock();
                ComplexSetterMethod(value);
                SyncObj.ExitWriteLock();
            }
        }
    }

    // more properties here ...

    private string ComplexGetterMethod()
    {
        // This method is not thread-safe and reads
        // multiple values, calculates stuff, ect. 
    }

    private void ComplexSetterMethod(string newValue)    
    {
        // This method is not thread-safe and reads
        // and writes multiple values.
    }
}

// =====================================

public static SomeClass AClass = new SomeClass();
public void SomeMultiThreadFunction()
{
    ...
    // access with locking from within the setter
    AClass.AProperty = "new value";
    ...
    // locking from outside of the class to increase performance
    AClass.SyncObj.EnterWriteLock();
    AClass.AProperty = "new value 2";
    AClass.AnotherProperty = "...";
    ...
    AClass.SyncObj.ExitWriteLock();
    ...
}

为了避免在我获取或设置多个属性时不必要的锁定,我发布了ReaderWriterLockSlim - 对象,并在每次我要获取或设置一堆属性时从类外部锁定它。为实现此目的,我的getter和setter方法使用IsReadLockHeld属性和IsWriteLockHeld ReaderWriterLockSlim属性检查是否已获取锁定。这很好,并且提高了我的代码的性能。

到目前为止一直很好,但是当我重新阅读有关IsReadLockHeldIsWriteLockHeld的文档时,我注意到了Microsoft的评论:

  

此属性旨在用于断言或其他调试   目的。不要用它来控制程序执行的流程。

我的问题是:为什么我不应该为此目的使用IsReadLockHeld/IsWriteLockHeld?我的代码有什么问题吗?一切都按预期工作,比使用递归锁(LockRecursionPolicy.SupportsRecursion)快得多。

澄清一下:这是一个很小的例子。我不想知道锁本身是否必要,或者是否可以以不同方式移除或实现。我只是想知道为什么我不应该使用IsReadLockHeld / IsWriteLockHeld来控制文档所述的程序流程。

2 个答案:

答案 0 :(得分:13)

经过一些进一步的研究后,我在German Support Forum of the Microsoft Developer Network上发布了同样的问题,并与非常有帮助的主持人 Marcel Roma 进行了讨论。他能够联系撰写此答案的ReaderWriterLockSlim Joe Duffy 的程序员:

  

我担心我的答案会留下一些不足之处。

     

该物业运作正常,并记录在案。指导真的很公正   因为有条件的获取和释放锁定往往是错误的   在实践中容易出错,特别是在抛出异常的情况下   混合。

     

构建代码通常是个好主意   使用递归获取,或者你没有,(当然后者是   总是更容易推理);使用像IsReadLockHeld这样的属性   你把它放在中间的某个地方。

     

我是RWLS的主要设计师之一,我不得不承认它   太多的花里胡哨了。我不一定后悔补充   IsReadLockHeld - 因为它可以派上用场进行调试和断言    - 但是一旦我们添加它,Pandora的盒子就被打开了,我们RWLS立即开启了这种用途。

     

我并不感到惊讶,人们想要使用它,如图所示   StackOverflow线程,我确信有一些合法的场景   它比替代品更好地工作。我只是建议犯错误   不使用它的一面。

总结一下:您可以使用IsReadLockHeldIsWriteLockHeld属性有条件地获取锁定,一切都会正常工作,但编程样式不好,一个人应该避免它。坚持递归或非递归锁定更好。要保持良好的编码样式,IsReadLockHeldIsWriteLockHeld应仅用于调试目的。

我要再次感谢Marcel Roma和Joe Duffy的宝贵帮助。

答案 1 :(得分:-2)

文档建议你做对了。

考虑以下交错执行。

Thread1.AcqrireReadLock();
Thread1.ComplexGetterMethod();
Thread2.ReadIsReaderLockHeldProperty();
Thread1.ReleaseReadLock();
Thread2.ComplexGetterMethod(); // performing read without lock.

我看到你的代码的另一个错误是

SyncObj.EnterReadLock();
try
{
    return ComplexGetterMethod();
}
finally
{
    SyncObj.ExitReadLock();
}

不是正确的做事方式。这是一个权利:

try
{
    SyncObj.EnterReadLock();

    return ComplexGetterMethod();
}
finally
{
    if (SyncObj.IsReadLockHeld)
        SyncObj.ExitReadLock();
}

这应该是你的getter方法的确切定义。