C# 微软';s对ReaderWriterLockSlim.isReadLockHold/isWriteLockHold的评论及其后果

C# 微软';s对ReaderWriterLockSlim.isReadLockHold/isWriteLockHold的评论及其后果,c#,readerwriterlockslim,C#,Readerwriterlockslim,要同步对我的属性的访问,我使用类。我使用以下代码以线程安全的方式访问我的属性 public class SomeClass { public readonly ReaderWriterLockSlim SyncObj = new ReaderWriterLockSlim(); public string AProperty { get { if (SyncObj.IsReadLockHeld)

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

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方法使用
readerwriterlocksim
isreadlockhold
属性和
iswritelockhold
属性检查是否已获取锁。这很好,提高了代码的性能

到目前为止还不错,但当我重新阅读有关
isreadlockhold
iswritelockhold
的文档时,我注意到微软的评论:

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

Thread1.AcqrireReadLock();
Thread1.ComplexGetterMethod();
Thread2.ReadIsReaderLockHeldProperty();
Thread1.ReleaseReadLock();
Thread2.ComplexGetterMethod(); // performing read without lock.
我的问题是:是否有理由不使用
isreadlockhold/iswritelockhold
?我的代码有什么问题吗?一切都按预期运行,比使用递归锁快得多(
LockRecursionPolicy.SupportsRecursion


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

文档为您提供了正确的建议

请考虑以下交错执行

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方法的确切定义。

经过进一步的研究,我在网上发布了同样的问题,并与非常有用的主持人Marcel Roma进行了讨论。他能够联系到写下以下答案的
ReaderWriterLockSlim
Joe Duffy的程序员:

我恐怕我的回答会留下一些不尽如人意的地方

该物业运作良好,符合文件规定。指导真的很公正 因为有条件地获取和释放锁往往是有缺陷的 而且在实践中容易出错,特别是在将异常抛出到 混合

通常,构造代码的结构是一个好主意,以便 使用递归获取,或者不使用(当然后者是 总是更容易推理);使用像isReadLockHold这样的属性 把你放在中间的某个地方。< /P> 我是RWLS的主要设计师之一,我不得不承认它是 太多的钟声和口哨声了。我不一定会后悔添加 IsReadLockHold——因为它可以方便地用于调试和断言 --然而,我们一加上它,潘多拉的盒子就被打开了,我们RWLS立刻被打开了,接受这种使用

我并不感到惊讶,人们希望使用它,如图所示 StackOverflow线程,我确信有一些合法的场景 它比其他替代方案更有效。我只是建议你犯错误 不使用它的一面

总而言之:您可以使用
isreadlockhold
iswritelockhold
属性有条件地获取锁,一切都可以正常工作,但这是一种糟糕的编程风格,应该避免。最好坚持使用递归锁或非递归锁。为了保持良好的编码风格,
isreadlockhold
iswritelockhold
应仅用于调试目的


我想再次感谢马塞尔·罗马和乔·达菲的宝贵帮助

ReaderWriteLockSlim不保护任何东西,.NET已经承诺对象引用更新是原子的。它唯一轻微的副作用是setter更新将在其他线程中可见。只使用MemoryBarrier()要便宜得多。没有线程安全保证,完全删除SyncObj当然更好,因为它只会给人一种错误的安全感。@EricLippert:IsRead/WriteLockHold的文档建议在当前线程持有锁时返回
true
。如果
IsRead/writelockhold
返回true,那么在没有显式调用
exitrade/WriteLock
的情况下,该线程上的锁状态如何发生更改?@Iridium:I撤回我的评论;我现在真的不知道为什么文档中会有这样的警告。也就是说:最佳实践是按照文档中所说的去做。您会问“这个代码有什么问题吗?”。是的,有。试图猜测代码中有我们看不到的错误是非常徒劳无益的。
SyncObj
应该是只读的,这样就永远不会被替换掉。这一点更为正确,因为您已将其公开。这种执行不会以这种方式发生,因为只有当当前线程持有锁时,
isreadlockhold
才会返回
true