【问题标题】:Should synclock be used on properties?应该在属性上使用同步锁吗?
【发布时间】:2010-11-25 09:07:33
【问题描述】:

我一直在阅读,关于是否应该在属性上使用同步锁得到了相互矛盾的答案。

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

任何反馈都将不胜感激。

【问题讨论】:

  • 如果您使用的方法是SetX() 而不是X 属性设置器,您是否需要锁定(“SyncLock”)?没有完整的上下文,很难说。此外,如果不了解更大的上下文,就无法辨别单个锁是否解决/解决了真正的问题。 “线程很容易。并发很难。”
  • @pst 我已经阅读了一些关于用方法替换属性的内容,但似乎也无法得到明确的答案。据我了解,属性本质上是一种简单的方法。由于我已经使用属性/字段设计实现了应用程序,因此我想尝试保持这种方式。我想这个问题更多的是关于良好的设计实践和线程安全。感谢您的链接,我以为我已经进行了彻底的搜索,但从未见过!
  • 用方法替换属性的真正意义在于“告诉,不要问”。如果您需要对类中的数据进行处理,请将代码作为方法移入类中,而不是将其放入表单或模块中并让它使用属性来获取数据。请参阅c2.com/cgi/wiki?TellDontAsk 进行讨论。
  • @Jeffery hmmm... 这是一篇有趣的文章。我的应用程序依赖于状态行为,因此在鸽子中扔了一只猫。

标签: c# multithreading properties synclock


【解决方案1】:

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

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

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

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

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

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

【讨论】:

  • 谢谢,这是非常有用的信息,并且至少满足其中两个条件,因此锁定听起来像是要走的路。字段始终是私有的,但同一类中的某些方法正在修改字段。所有其他人都必须使用公共属性,因为它们也会在发生更改时触发事件。
  • 这让事情变得更清楚了,感谢您的帮助和快速响应。你们太棒了!
【解决方案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) {
            ...
        }
    }
}

【讨论】:

  • 很好......所以基本上你是说从属性中获取是没有问题的,但是每当我需要设置它时,我应该使用一个锁定“字段”的方法来设置它修改并因此绕过属性设置操作?
  • 您要确保任何更改对象的操作都是“原子的”并且不会在执行过程中被上下文更改中断。您可以通过锁定对象而不是字段来做到这一点。是否使用私有属性设置器或直接访问支持字段取决于您 - 它不会影响任何事情。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-09
  • 1970-01-01
  • 2022-09-27
  • 1970-01-01
  • 2011-01-21
  • 2017-01-14
相关资源
最近更新 更多