ReaderWriterLockSlim
类可用于多个线程同时读,但同一时间,只能有一个线程写的情况
ReaderWriterLockSlim
类支持三种锁定模式:Read
,Write
,UpgradeableRead
。这三种模式对应的方法分别为
- 进入:
EnterReadLock/TryEnterReadLock
,EnterWriteLock/TryEnterWriteLock
,EnterUpgradeableReadLock/TryEnterUpgradeableReadLock
- 退出:
ExitReadLock
,ExitWriteLock
,ExitUpgradeableReadLock
ReaderWriterLockSlim
类似于ReaderWriterLock
, 只是简化了递归、升级和降级锁定状态的规则。ReaderWriterLockSlim
可避免很多潜在的可能会产生死锁的情况。此外,ReaderWriterLockSlim
性能明显优于ReaderWriterLock
需要注意的点
- 在使用完
ReaderWriterLockSlim
对象之后,我们需要释放该对象拥有的资源(使用using
或手动调用Dispose
方法) - 尽可能的不使用递归策略,因为这种方式导致死锁的概率非常高
一个 ReaderWriterLockSlim
可以是以下四种状态之一
-
Not Entered
: 表示没有线程进入这个锁(也有可能所有线程都已经退出了) -
Read
:表示一个或多个用于读资源的线程进入了锁。一个线程可以通过EnterReadLock
或TryEnterReadLock
方法进入读模式 -
Upgrade
:此状态下,一次只能有一个线程可以升级为写模式,其他希望升级的线程将会被阻塞 -
Write
:表示一个具有写入权限的线程进入了锁。其他希望进入写入权限的线程将会被阻塞
示例代码如下
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
public class App {
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 {
if (innerCache.TryGetValue(key, out string 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();
}
}
public static void Main() {
var sc = new SynchronizedCache();
var tasks = new List<Task>();
int itemsWritten = 0;
// 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 = 1; ctr <= vegetables.Length; ctr++)
sc.Add(ctr, vegetables[ctr - 1]);
itemsWritten = vegetables.Length;
Console.WriteLine("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 = 0; ctr <= 1; 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 = 1;
step = 1;
last = items;
} else {
start = items;
step = -1;
last = 1;
}
for (int index = start; desc ? index >= last : index <= last; index += step)
output += String.Format("[{0}] ", sc.Read(index));
Console.WriteLine("Task {0} read {1} items: {2}\n", Task.CurrentId, items, output);
} while (items < itemsWritten | itemsWritten == 0);
}));
}
// Execute a red/update task.
tasks.Add(Task.Run(() => {
Thread.Sleep(100);
for (int ctr = 1; ctr <= sc.Count; ctr++) {
String value = sc.Read(ctr);
if (value == "cucumber")
if (sc.AddOrUpdate(ctr, "green bean") != SynchronizedCache.AddOrUpdateStatus.Unchanged)
Console.WriteLine("Changed 'cucumber' to 'green bean'");
}
}));
// Wait for all three tasks to complete.
Task.WaitAll(tasks.ToArray());
// Display the final contents of the cache.
Console.WriteLine();
Console.WriteLine("Values in synchronized cache: ");
for (int ctr = 1; ctr <= sc.Count; ctr++)
Console.WriteLine(" {0}: {1}", ctr, sc.Read(ctr));
}
}
在实际项目中,以上的 SynchronizedCache
类完全可以使用 ConcurrentDictionary<int, string>
来替代
至此,本节内容讲解完毕。
欢迎关注公众号【嘿嘿的学习日记】,所有的文章,都会在公众号首发,Thank you~
网友评论