2

我一直在阅读,并在是否应该或不应该在属性上使用同步锁的问题上得到相互矛盾的答案。

我有一个多线程应用程序,需要跨线程获取/设置实例对象的属性。它目前是在没有使用同步锁的情况下实现的,到目前为止我还没有注意到任何问题。我在常见的静态方法上使用同步锁,但我想以线程安全的方式正确实现我的实例类。

任何反馈将不胜感激。

4

2 回答 2

4

一个好的经验法则是,如果以下任何条件成立,您就需要锁定:

  • 如果要在多个线程上修改对象的任何字段
  • 如果任何修改涉及访问多个字段
  • 如果任何可修改字段是 Double、Decimal 或结构化值类型
  • 如果任何修改涉及读取-修改-写入(即添加到字段或使用另一个字段的值设置一个字段)

那么您可能需要锁定访问这些字段的每个方法或属性。

编辑:请记住,锁定类内部很少是足够的——您需要做的是确保在整个逻辑操作的范围内事情不会出错。

正如@Bevan 指出的那样,如果调用代码需要多次访问一个对象,则客户端代码应该在其工作的整个过程中对对象进行自己的锁定,以确保另一个线程不会“介于两者之间”它的访问并弄乱了它的逻辑。

您还需要注意,如果有任何东西需要一次取出多个锁,它们总是按相同的顺序进行。如果线程 1 在实例 A 上锁定并尝试锁定实例 B,而线程 2 在实例 B 上锁定并尝试在实例 A 上获得锁定,则两个线程都被卡住并且无法继续 - 你有一个死锁。

于 2010-11-25T00:44:12.353 回答
2

您不能仅通过用锁包围各个方法来使对象线程安全。您最终要做的就是序列化(减慢)对对象的访问。

考虑这个小例子:

var myObject = ...
var myThreadSafeList = ...
if (!myThreadSafeList.Contains(myObject))
{
    myThreadSafeList.Add(myObject);
}

即使 myThreadSafeList 锁定了每个方法,这也不是线程安全的,因为另一个线程可以在调用和之间更改列表的内容。Contains()Add()

在这个列表的情况下,需要一个额外的方法AddIfMissing()

var myObject = ...
var myThreadSafeList = ...
myThreadSafeList.AddIfMissing(myObject);

只有将逻辑移到对象中,才能用锁包围这两个操作并使其安全。

没有更多细节,很难进一步评论,但我建议如下:

  • 将所有属性设为只读,并允许任何人随时阅读
  • 提供 mutator 方法,这些方法采用一起修改的属性集,并在锁内以原子方式进行更改

为了显示:

public class Person {
    public string FullName { get; private set; }
    public string FamilyName { get; private set; }
    public string KnownAs { get; private set; }

    public void SetNames( string full, string family, string known) {
        lock (padLock) {
            ...
        }
    }
}
于 2010-11-25T00:53:06.617 回答