我正在尝试实现许多利用ReaderWriterLockSlim来实现线程安全的属性。所以像大多数人一样,我最终会在我所有的财产中得到这样的东西:
public string Name
{
get
{
rwLock.EnterReadLock();
try {
return name;
}
finally {
rwLock.ExitReadLock();
}
}
set
{
rwLock.EnterWriteLock();
try {
name = value;
}
finally {
rwLock.ExitWriteLock();
}
}
}
这在 10 个属性中感觉非常冗长和重复,所以我正在寻找一个更 DRY 的实现。
显而易见的解决方案是将其包装到一个类中,该类释放 dispose 上的锁,并允许我将线程安全操作放在 using 语句中。显然,根据这个和其他一些来源,这不是很安全。
所以我试图使用 lambda 表达式和匿名方法提出一个漂亮的解决方案:
private TResult ThreadSafeRead<TResult>(Func<TResult> value)
{
rwLock.EnterReadLock();
try {
return value();
}
finally {
rwLock.ExitReadLock();
}
}
private void ThreadSafeWrite(Action value)
{
rwLock.EnterWriteLock();
try {
value();
}
finally {
rwLock.ExitWriteLock();
}
}
public string Name
{
get { return ThreadSafeRead(() => name); }
set { ThreadSafeWrite(() => { name = value; }); }
}
虽然这消除了让我烦恼的重复代码,但我不知道它是否像我原来的详细实现一样线程安全。
是否有人对MSIL有更深入的了解,这将生成和多线程理论上能够告诉我我的实施是否安全?
我看到人们这样做的通常方法是将锁包裹在一个对象中并执行以下操作:
using (var obj = new ReadOnlyLock(this.rwLock))
{
this.name = value;
}
我承认我不这样做(为什么要创建另一个对象只是为了锁定?),但它确实实现了一些人认为最重要的更好的风格目标......
您的博客文章中提到的问题确实存在,是的。有关此主题的更彻底处理,请参阅Joe Duffy的帖子。
您的代码甚至会遇到异步异常吗?它们很可能仅在 ASP.NET 下运行时发生,并且当前请求由于超时而中止。当然,由于各种原因,您永远不应该自己中止线程。
我认为你不应该实际关注这个问题。