【发布时间】:2010-01-25 20:04:40
【问题描述】:
通常,当我想要一个线程安全的类时,我会执行以下操作:
public class ThreadSafeClass
{
private readonly object theLock = new object();
private double propertyA;
public double PropertyA
{
get
{
lock (theLock)
{
return propertyA;
}
}
set
{
lock (theLock)
{
propertyA = value;
}
}
}
private double propertyB;
public double PropertyB
{
get
{
lock (theLock)
{
return propertyB;
}
}
set
{
lock (theLock)
{
propertyB = value;
}
}
}
public void SomeMethod()
{
lock (theLock)
{
PropertyA = 2.0 * PropertyB;
}
}
}
它有效,但它非常冗长。有时我什至会为每个方法和属性创建一个锁对象,从而产生更多的冗长和复杂性。
我知道也可以使用 Synchronization 属性锁定类,但我不确定它的扩展性如何——因为我经常期望有数十万甚至数百万个线程安全对象实例.这种方法将为类的每个实例创建一个同步上下文,并要求该类从 ContextBoundObject 派生,因此不能从其他任何东西派生——因为 C# 不允许多重继承——这是一个显示停止器在很多情况下。
编辑:正如一些响应者所强调的,没有“灵丹妙药”线程安全类设计。我只是想了解我使用的模式是否是好的解决方案之一。当然,任何特定情况下的最佳解决方案都取决于问题。以下几个答案包含应考虑的替代设计。
编辑:此外,线程安全的定义不止一种。例如,在我上面的实现中,以下代码不是线程安全的:
var myObject = new ThreadSafeClass();
myObject.PropertyA++; // NOT thread-safe
那么,上面的类定义代表了一个好的方法吗?如果不是,对于具有类似行为的设计,对于一组类似的用途来说是线程安全的,您会推荐什么?
【问题讨论】:
-
请先搜索:stackoverflow.com/…
-
嗯。在您的代码中,您使用相同的锁定对象来读取 PropertyA 和 PropertyB。这意味着读取 PropertyA 的一个线程将锁定另一个尝试读取 PropertyB 的线程,直到第一个线程完成。那是你要的吗?如果这是我的课,我会为每个 getter 创建一个单独的读取锁,然后设置器也必须尊重该锁。请参阅下面的答案。
-
我只是对你投了反对票,因为没有单一的方法可以生成线程安全的类。对这个问题的任何回答都有可能给未来的读者留下错误的印象。
-
约翰,我同意你的观点,并根据你的评论编辑了我上面的问题。
-
我的意思不是有多个解决方案——我的意思是线程安全的定义不止一个。您的问题表明只有一个。
标签: c# .net multithreading thread-safety