1. ホーム
  2. c#

[解決済み] ReaderWriterLockSlimが単純なロックより優れているのはどのような場合ですか?

2023-07-01 17:23:24

質問

私はこのコードでReaderWriterLockの非常に愚かなベンチマークを行っており、読み込みが書き込みの4倍の頻度で発生しています。

class Program
{
    static void Main()
    {
        ISynchro[] test = { new Locked(), new RWLocked() };

        Stopwatch sw = new Stopwatch();

        foreach ( var isynchro in test )
        {
            sw.Reset();
            sw.Start();
            Thread w1 = new Thread( new ParameterizedThreadStart( WriteThread ) );
            w1.Start( isynchro );

            Thread w2 = new Thread( new ParameterizedThreadStart( WriteThread ) );
            w2.Start( isynchro );

            Thread r1 = new Thread( new ParameterizedThreadStart( ReadThread ) );
            r1.Start( isynchro );

            Thread r2 = new Thread( new ParameterizedThreadStart( ReadThread ) );
            r2.Start( isynchro );

            w1.Join();
            w2.Join();
            r1.Join();
            r2.Join();
            sw.Stop();

            Console.WriteLine( isynchro.ToString() + ": " + sw.ElapsedMilliseconds.ToString() + "ms." );
        }

        Console.WriteLine( "End" );
        Console.ReadKey( true );
    }

    static void ReadThread(Object o)
    {
        ISynchro synchro = (ISynchro)o;

        for ( int i = 0; i < 500; i++ )
        {
            Int32? value = synchro.Get( i );
            Thread.Sleep( 50 );
        }
    }

    static void WriteThread( Object o )
    {
        ISynchro synchro = (ISynchro)o;

        for ( int i = 0; i < 125; i++ )
        {
            synchro.Add( i );
            Thread.Sleep( 200 );
        }
    }

}

interface ISynchro
{
    void Add( Int32 value );
    Int32? Get( Int32 index );
}

class Locked:List<Int32>, ISynchro
{
    readonly Object locker = new object();

    #region ISynchro Members

    public new void Add( int value )
    {
        lock ( locker ) 
            base.Add( value );
    }

    public int? Get( int index )
    {
        lock ( locker )
        {
            if ( this.Count <= index )
                return null;
            return this[ index ];
        }
    }

    #endregion
    public override string ToString()
    {
        return "Locked";
    }
}

class RWLocked : List<Int32>, ISynchro
{
    ReaderWriterLockSlim locker = new ReaderWriterLockSlim();

    #region ISynchro Members

    public new void Add( int value )
    {
        try
        {
            locker.EnterWriteLock();
            base.Add( value );
        }
        finally
        {
            locker.ExitWriteLock();
        }
    }

    public int? Get( int index )
    {
        try
        {
            locker.EnterReadLock();
            if ( this.Count <= index )
                return null;
            return this[ index ];
        }
        finally
        {
            locker.ExitReadLock();
        }
    }

    #endregion

    public override string ToString()
    {
        return "RW Locked";
    }
}

でも、どちらも多かれ少なかれ同じようなパフォーマンスをすることはわかります。

Locked: 25003ms.
RW Locked: 25002ms.
End

読み込みを書き込みの20倍にしても、性能は(ほぼ)同じです。

私はここで何か間違ったことをしているのでしょうか?

よろしくお願いします。

どのように解決するのですか?

あなたの例では、スリープは次のことを意味します。 一般的に は競合がないことを意味します。競合のないロックは非常に高速です。これが問題になるには 競合する ロックが必要で、もし の書き込み がある場合、それらはほぼ同じになるはずです ( lock の方が速いかもしれない) - しかし、それが ほとんど 読み出しである場合 (まれに書き込みの競合がある)、私は ReaderWriterLockSlim ロックが lock .

個人的には、参照スワッピングを使用する別の戦略を好みます。書き込みは クローン コピーに変更を加え、その後 Interlocked.CompareExchange を使って参照を交換します (別のスレッドがその間に参照を変更した場合、その変更を再適用します)。