14

为了同步对我的属性的访问,我使用了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-Object 并在每次我要获取或设置一堆属性时从类外部锁定它。为了实现这一点,我的 getter 和 setter 方法检查是否已使用IsReadLockHeld属性IsWriteLockHeldReaderWriterLockSlim. 这工作正常,并提高了我的代码的性能。

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

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

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

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

4

2 回答 2

14

经过一些进一步的研究,我在微软开发者网络的德国支持论坛上发布了同样的问题,并与非常有帮助的主持人Marcel Roma进行了讨论。他能够联系写此答案的ReaderWriterLockSlim Joe Duffy的程序员:

恐怕我的回答可能会留下一些不足之处。

该物业工作正常,并记录在案。该指南实际上只是因为在实践中,有条件地获取和释放锁往往是错误的和容易出错的,特别是在混入异常的情况下。

构造代码通常是一个好主意,这样您要么使用递归获取,要么不使用,(当然后者总是更容易推理);使用 IsReadLockHeld 之类的属性会使您处于中间位置。

我是 RWLS 的主要设计师之一,我不得不承认它有太多的花里胡哨。我不一定会后悔添加 IsReadLockHeld——因为它可以在调试和断言中派上用场——但是一旦我们添加它,潘多拉的盒子就被打开了,我们的 RWLS 立即打开了这种用法。

我对人们想要使用 StackOverflow 线程中所示的那样使用它并不感到惊讶,而且我确信在一些合法的场景中它比替代方案效果更好。我只是建议不要使用它。

总结一下:您可以使用IsReadLockHeldandIsWriteLockHeld属性有条件地获取锁,一切都会正常工作,但这是一种糟糕的编程风格,应该避免它。最好坚持使用递归或非递归锁。保持良好的编码风格IsReadLockHeldIsWriteLockHeld只应用于调试目的。

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

于 2013-06-25T11:27:06.467 回答
-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 方法的确切定义。

于 2013-06-16T13:09:30.827 回答