C# ReaderWriterLockSlim何时比简单锁更好?
我用这段代码在ReaderWriterLock上做了一个非常愚蠢的基准测试,其中读取的频率是写入的4倍:C# ReaderWriterLockSlim何时比简单锁更好?,c#,.net,multithreading,locking,C#,.net,Multithreading,Locking,我用这段代码在ReaderWriterLock上做了一个非常愚蠢的基准测试,其中读取的频率是写入的4倍: class Program { static void Main() { ISynchro[] test = { new Locked(), new RWLocked() }; Stopwatch sw = new Stopwatch(); foreach ( var isynchro in test ) {
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";
}
}
即使使读的次数比写的次数多20倍,性能仍然(几乎)相同
我做错什么了吗
问候您。我想这是因为您的读写线程中存在睡眠。
你的读线程有一个500tims 50ms的睡眠时间,在它睡眠的大部分时间是25000编辑2:只需删除
线程。睡眠调用ReadThread
和WriteThread
,我看到Locked
的性能优于RWLocked
。我相信这是一针见血;您的方法太快,不会产生争用。当我将线程.Sleep(1)
添加到Get
和Add
方法的Locked
和RWLocked
(使用4个读线程和1个写线程)中时,RWLocked
将Locked
的裤子打下来
编辑:好吧,如果我第一次发布这个答案时真的在想,我至少会意识到为什么你要把线程。Sleep
调用放在那里:你试图重现读比写更频繁的场景。这不是正确的方法。相反,我会为Add
和Get
方法引入额外的开销,以创建更大的争用机会(as),创建更多的读线程而不是写线程(以确保读线程比写线程更频繁),并删除线程的Sleep
调用ReadThread
(这实际上减少了争用,实现了与您想要的相反的效果)
我喜欢你到目前为止所做的一切。但这里有几个问题我一眼就能看到:
为什么要调用Thread.Sleep
呢?这些调用只会使执行时间不断膨胀,这会人为地使性能结果趋于一致
我也不会在由您的秒表测量的代码中包含新线程
对象的创建。这不是一个简单的创建对象
我不知道,一旦你解决了上述两个问题,你是否会看到显著的不同。但我认为,在继续讨论之前,应该先解决它们。查看这篇文章:
您的睡眠时间可能足够长,使得锁定/解锁在统计上无关紧要。在您的示例中,睡眠意味着通常不存在争用。无争用的锁非常快。为此,您需要争用的锁;如果争用中有写入,它们应该大致相同(lock
可能更快)-但是如果它主要是读取(很少有写入争用),我希望ReaderWriterLockSlim
lock能够执行lock
就我个人而言,我更喜欢这里的另一种策略,使用引用交换-因此读取总是可以在不检查/锁定等的情况下读取。写入将其更改为克隆副本,然后使用互锁。CompareExchange
交换引用(如果另一个线程在此期间更改了引用,则重新应用其更改).无竞争的锁的获取时间为微秒,因此,调用Sleep
将使执行时间相形见绌,除非您有多核硬件(或至少与计划的生产环境相同),否则无法在此处进行实际测试
一个更合理的测试是,通过在锁中放置一个短暂的延迟来延长锁定操作的生命周期。这样,您就可以真正对比使用ReaderWriterLockSlim添加的并行性与basiclock()
所隐含的序列化
目前,被锁定的操作所花费的时间会在锁外发生的休眠调用所产生的噪音中丢失。在这两种情况下,总时间大多与休眠相关
你确定你的真实世界应用程序的读写次数相等吗?ReaderWriterLockSlim
对于拥有多个读卡器和相对不常使用的写卡器的情况来说确实更好。1个写卡器线程与3个读卡器线程的对比应该能更好地证明ReaderWriterLockSlim
的好处,但无论如何,你的测试结果都是正确的uld符合您预期的真实访问模式。此程序中没有争用。Get和Add方法在几纳秒内执行。多个线程在准确时间命中这些方法的几率非常小
放入一个Thread.Sleep(1)调用它们,并从线程中移除睡眠以查看差异。我自己的测试表明,ReaderWriterLockSlim
的开销大约是普通锁的5倍。这意味着RWL要优于普通的旧锁,通常会出现以下情况
- 读者的数量大大超过了作者
- 锁必须保持足够长的时间以克服额外的开销
在大多数实际应用程序中,这两个条件不足以克服额外的开销。具体来说,在您的代码中,锁的持有时间很短,锁的开销可能是主要因素。如果要将这些线程.Sleep
调用移到锁内,则可能会得到d不同的结果。如果锁定需要较长时间执行的部分代码,则使用ReaderWriterLockSlim
会比使用简单的锁获得更好的性能。在这种情况下,读卡器可以并行工作。获取ReaderWriterLockSlim
比输入简单的监视器花费更多的时间
Locked: 25003ms.
RW Locked: 25002ms.
End