vto*_*ola 74 .net c# multithreading locking
我正在使用此代码对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";
}
}
Run Code Online (Sandbox Code Playgroud)
但我认为两者的表现方式大致相同:
Locked: 25003ms.
RW Locked: 25002ms.
End
Run Code Online (Sandbox Code Playgroud)
即使写入的读取次数增加20倍,性能仍然(几乎)相同.
我在这里做错了吗?
亲切的问候.
Mar*_*ell 97
在您的示例中,睡眠意味着通常没有争用.无竞争锁定速度非常快.为此,你需要一个竞争锁; 如果在该争用中存在写入,它们应该大致相同(lock甚至可能更快) - 但如果它主要是读取(很少写入争用),我会期望ReaderWriterLockSlim锁定超出lock.
就个人而言,我更喜欢这里的另一种策略,使用引用交换 - 所以读取总是可以在没有检查/锁定等的情况下读取.写入使其更改为克隆副本,然后用于Interlocked.CompareExchange交换引用(如果另一个线程重新应用它们的更改)在过渡期间改变了参考文献).
Bri*_*eon 21
我自己的测试表明,ReaderWriterLockSlim与正常情况相比,其开销约为5倍lock.这意味着RWLS的性能优于普通的旧锁,通常会发生以下情况.
在大多数实际应用中,这两个条件不足以克服额外的开销.特别是在你的代码中,锁被保持这么短的时间,锁定开销可能是主导因素.如果你要将这些Thread.Sleep调用移到锁内,那么你可能会得到不同的结果.
Dan*_*Tao 10
编辑2:只需删除Thread.Sleep来自ReadThread和的电话WriteThread,我看到Locked表现优异RWLocked.我相信汉斯在这里击中了钉子; 你的方法太快,不会产生争用.当我添加Thread.Sleep(1)到Get和的Add方法Locked和RWLocked(并使用4个读取线程对抗1个写线程)时,RWLocked打败了裤子Locked.
编辑:好的,如果我真的在想我第一次发布这个答案的时候,我至少会意识到你为什么要把这些Thread.Sleep调用放在那里:你试图重新发现读取的场景比写入更频繁.这不是正确的方法.相反,我会引入额外的开销,你Add和Get方法来创建争的机会较大(如汉斯建议),创造更多的读取线程,比写线程(以确保更频繁的读比写),并删除Thread.Sleep从通话ReadThread和WriteThread(事实上,减少争用,实现与你想要的相反).
我喜欢你到目前为止所做的一切.但是这里有一些我看到的问题:
Thread.Sleep打电话?这些只会使您的执行时间膨胀一个恒定的数量,这将人为地使性能结果收敛.Thread在你的测量代码中包含新对象的创建Stopwatch.这不是一个创造的微不足道的对象.一旦你解决了上述两个问题,你是否会看到显着的差异,我不知道.但我相信在讨论继续之前应该解决这些问题.
小智 8
ReaderWriterLockSlim如果您锁定需要更长时间执行的代码部分,您将获得比简单锁定更好的性能.在这种情况下,读者可以并行工作.获取ReaderWriterLockSlim比进入简单需要更多时间Monitor.检查我的ReaderWriterLockTiny实现是否有读取器 - 写入器锁,它比简单的锁定语句更快,并提供读写器功能:http://i255.wordpress.com/2013/10/05/fast-readerwriterlock-for-net/