.NET 同步与异步之锁(ReaderWriterLockSlim)(八)

时间:2023-03-09 07:57:58
.NET 同步与异步之锁(ReaderWriterLockSlim)(八)

本随笔续接:.NET 同步与异步之锁(Lock、Monitor)(七)

由于锁 ( lock 和 Monitor ) 是线程独占式访问的,所以其对性能的影响还是蛮大的,那有没有一种方式可是实现:允许多个线程同时读数据、只允许一个线程写数据呢?答案是肯定的。

读写锁 ReaderWriterLock 、就是 支持单个写线程和多个读线程的锁。自.NET 3.5 开始 ReaderWriterLockSlim、登上舞台,ReaderWriterLockSlim 可以看做是 ReaderWriterLock 的升级版。 由于 ReaderWriterLockSlim  默认不支持递归调用、所以在某种意义上来说更不容易造成死锁。

一、先看一下demo(来源msdn代码示例):

    public class SynchronizedCache
{
private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();
private Dictionary<int, string> innerCache = new Dictionary<int, string>(); public int Count
{ get { return innerCache.Count; } } public string Read(int key)
{
cacheLock.EnterReadLock();
try
{
return innerCache[key];
}
finally
{
cacheLock.ExitReadLock();
}
} public void Add(int key, string value)
{
cacheLock.EnterWriteLock();
try
{
innerCache.Add(key, value);
}
finally
{
cacheLock.ExitWriteLock();
}
} public bool AddWithTimeout(int key, string value, int timeout)
{
if (cacheLock.TryEnterWriteLock(timeout))
{
try
{
innerCache.Add(key, value);
}
finally
{
cacheLock.ExitWriteLock();
}
return true;
}
else
{
return false;
}
} public AddOrUpdateStatus AddOrUpdate(int key, string value)
{
cacheLock.EnterUpgradeableReadLock();
try
{
string result = null;
if (innerCache.TryGetValue(key, out result))
{
if (result == value)
{
return AddOrUpdateStatus.Unchanged;
}
else
{
cacheLock.EnterWriteLock();
try
{
innerCache[key] = value;
}
finally
{
cacheLock.ExitWriteLock();
}
return AddOrUpdateStatus.Updated;
}
}
else
{
cacheLock.EnterWriteLock();
try
{
innerCache.Add(key, value);
}
finally
{
cacheLock.ExitWriteLock();
}
return AddOrUpdateStatus.Added;
}
}
finally
{
cacheLock.ExitUpgradeableReadLock();
}
} public void Delete(int key)
{
cacheLock.EnterWriteLock();
try
{
innerCache.Remove(key);
}
finally
{
cacheLock.ExitWriteLock();
}
} public enum AddOrUpdateStatus
{
Added,
Updated,
Unchanged
}; ~SynchronizedCache()
{
if (cacheLock != null) cacheLock.Dispose();
}
} private void ReaderWriterLock()
{
var sc = new SynchronizedCache();
var tasks = new List<Task>();
int itemsWritten = ; // Execute a writer.
tasks.Add(Task.Run(() =>
{
String[] vegetables = { "broccoli", "cauliflower",
"carrot", "sorrel", "baby turnip",
"beet", "brussel sprout",
"cabbage", "plantain",
"spinach", "grape leaves",
"lime leaves", "corn",
"radish", "cucumber",
"raddichio", "lima beans" };
for (int ctr = ; ctr <= vegetables.Length; ctr++)
sc.Add(ctr, vegetables[ctr - ]); itemsWritten = vegetables.Length; base.PrintInfo(string.Format("Task {0} wrote {1} items\n", Task.CurrentId, itemsWritten));
}));
// Execute two readers, one to read from first to last and the second from last to first.
for (int ctr = ; ctr <= ; ctr++)
{
bool desc = Convert.ToBoolean(ctr);
tasks.Add(Task.Run(() =>
{
int start, last, step;
int items;
do
{
String output = String.Empty;
items = sc.Count;
if (!desc)
{
start = ;
step = ;
last = items;
}
else
{
start = items;
step = -;
last = ;
} for (int index = start; desc ? index >= last : index <= last; index += step)
output += String.Format("[{0}] ", sc.Read(index)); base.PrintInfo(string.Format("Task {0} read {1} items: {2}\n", Task.CurrentId, items, output)); } while (items < itemsWritten | itemsWritten == );
}));
}
// Execute a red/update task.
tasks.Add(Task.Run(() =>
{
Thread.Sleep();
for (int ctr = ; ctr <= sc.Count; ctr++)
{
String value = sc.Read(ctr);
if (value == "cucumber")
if (sc.AddOrUpdate(ctr, "green bean") != SynchronizedCache.AddOrUpdateStatus.Unchanged)
base.PrintInfo("Changed 'cucumber' to 'green bean'");
}
})); // Wait for all three tasks to complete.
Task.WaitAll(tasks.ToArray()); // Display the final contents of the cache.
base.PrintInfo("");
base.PrintInfo("Values in synchronized cache: ");
for (int ctr = ; ctr <= sc.Count; ctr++)
base.PrintInfo(string.Format(" {0}: {1}", ctr, sc.Read(ctr)));
}

Demo

二、通过Demo我们来看一下 ReaderWriterLockSlim 的用法:

1、EnterWriteLock  进入写模式锁定状态

2、EnterReadLock   进入读模式锁定状态

3、EnterUpgradeableReadLock 进入可升级的读模式锁定状态

并且三种锁定模式都有超时机制、对应 Try... 方法,退出相应的模式则使用 Exit... 方法,而且所有的方法都必须是成对出现的。

三、备注及注意事项

1、对于同一把锁、多个线程可同时进入 读模式。

2、对于同一把锁、同时只允许一个线程进入 写模式。

3、对于同一把锁、同时只允许一个线程进入 可升级的读模式。

4、通过默认构造函数创建的读写锁是不支持递归的,若想支持递归 可通过构造 ReaderWriterLockSlim(LockRecursionPolicy) 创建实例。

5、对于同一把锁、同一线程不可两次进入同一锁状态(开启递归后可以)

6、对于同一把锁、即便开启了递归、也不可以在进入读模式后再次进入写模式或者可升级的读模式(在这之前必须退出读模式)。

7、再次强调、不建议启用递归。

8、读写锁具有线程关联性,即 两个线程间拥有的锁的状态 相互独立不受影响、并且不能相互修改其锁的状态。

9、升级状态:在进入可升级的读模式 EnterUpgradeableReadLock 后,可在恰当时间点 通过 EnterWriteLock  进入写模式。

10、降级状态:可升级的读模式可以降级为读模式:即 在进入可升级的读模式 EnterUpgradeableReadLock 后, 通过首先调用读取模式 EnterReadLock 方法,然后再调用 ExitUpgradeableReadLock 方法。

随笔暂告一段落、下一篇随笔介绍:轻量级的锁(Interlocked、SpinLock)(预计1篇随笔)

附,Demo : http://files.cnblogs.com/files/08shiyan/ParallelDemo.zip

参见更多:随笔导读:同步与异步

(未完待续...)