基本的命名锁类






3.29/5 (4投票s)
这是一个用于获取命名锁的实用类。
引言
我遇到了一些需要同步特定“名称”的情况。我参与的一个应用程序大量使用 Lucene.NET;每个页面显示几个查询的结果。同时对 Lucene 运行多个相同的查询没有太大意义,所以我为每个查询生成一个键,针对该键进行加锁,并让第一个线程执行实际操作,而其他线程则悠闲地喝咖啡。
使用代码
使用这个类非常简单。创建一个新的 NamedLock<string>
实例,然后在 using
语句中调用 Lock
函数。
NamedLock<string> locker = new NamedLock<string>();
var url = "http://services.digg.com...";
using (locker.Lock(url))
{
//Do something synchronized
var xml = new XmlDocument();
xml.Load(url);
}
关注点
请查看类本身。它相对较小,包含三部分:
- 包含
Lock
和Unlock
函数的主要类 - 实现
IDisposable
的内部Token
类 - 内部
ReferenceCount
类
NamedLock
非常简单。它包含一个 Dictionary<T, ReferenceCount>
来跟踪当前锁定的名称,并提供用于获取和释放锁的实用函数。Lock
的代码如下:
public IDisposable Lock(T name, int timeout)
{
Monitor.Enter(lockCollection);
ReferenceCount obj = null;
lockCollection.TryGetValue(name, out obj);
if (obj == null)
{
obj = new ReferenceCount();
Monitor.Enter(obj);
lockCollection.Add(name, obj);
Monitor.Exit(lockCollection);
}
else
{
obj.AddRef();
Monitor.Exit(lockCollection);
if (!Monitor.TryEnter(obj, timeout))
{
throw new TimeoutException(
string.Format(
"Timeout while waiting for lock on {0}",
name)
);
}
}
return new Token<T>(this, name);
}
此函数锁定 lockCollection
,检查是否存在具有相同名称的现有锁,如果它是第一个,则添加一个,然后锁定并返回一个令牌。它使用 Monitor.Enter
而不是简单的 lock
语句,原因很好:您会注意到,如果集合中没有当前锁,我们实际上会在释放集合上的锁之前锁定同步对象(命名为 obj
)。如果锁存在于集合中,我们会递增引用计数器,释放集合锁,然后才锁定同步对象。这样做可以避免对锁集合的死锁(不妙)。
Unlock
函数也很简单明了
public void Unlock(T name)
{
lock (lockCollection)
{
ReferenceCount obj = null;
lockCollection.TryGetValue(name, out obj);
if (obj != null)
{
Monitor.Exit(obj);
if (0 == obj.Release())
{
lockCollection.Remove(name);
}
}
}
}
它锁定 lockCollection
,获取同步对象,释放命名锁,然后在没有其他线程持有对其引用时删除同步对象。这段代码更直接,因为我们不必做任何花招来避免锁集合上的死锁。
令牌类非常简单,我甚至懒得把它粘贴在这里。它接受对父级 NamedLock
的引用,然后在释放时调用 parent.Unlock(name)
。
历史
- 最初发布在我的博客上。