【问题标题】:C# 4.0: Are there ready-made, thread-safe autoimplemented properties?C# 4.0:是否有现成的、线程安全的自动实现属性?
【发布时间】:2011-05-17 07:13:04
【问题描述】:

我想对自动实现的属性进行线程安全的读写访问。即使在最新版本中,我也缺少 C#/.NET 框架中的此功能。 充其量,我会期待类似的东西

[Threadsafe]
public int? MyProperty { get; set; }

我知道有各种代码示例可以实现这一点,但 我只是想确保在我自己实现某些东西之前,仅使用 .NET 框架方法仍然无法做到这一点。我错了吗?

编辑:由于一些答案详细说明了原子性,我想说我只想拥有它,据我所知:只要(并且不超过)一个线程正在读取属性的值,不允许其他线程更改该值。因此,多线程不会引入无效值。我选择了int?输入,因为这是我目前关心的。

EDIT2:I have found the specific answer to the example with Nullable here, by Eric Lippert

【问题讨论】:

    标签: c# .net multithreading thread-safety properties


    【解决方案1】:

    正确;没有这样的设备。大概您正试图防止在另一个线程更改了它的一半(原子性)时读取该字段?请注意,许多(小)原语对于这种类型的线程问题本质上是安全的:

    5.5 变量引用的原子性

    读取和写入以下数据 类型是原子的:boolcharbytesbyte, short, ushort, uint, int, float引用类型。在 枚举的加法、读写 具有基础类型的类型 之前的列表也是原子的。

    但老实说,这只是冰山一角; 本身仅仅拥有一个线程安全的属性通常是不够的;大多数情况下,同步块的范围必须更多,而不仅仅是一次读/写。

    根据访问配置文件,还有很多不同的方法可以使某些东西成为线程安全的;

    • lock?
    • ReaderWriterLockSlim?
    • 引用交换到某个类(本质上是Box<T>,在这种情况下是Box<int?>
    • Interlocked(各种伪装)
    • volatile(在某些情况下;它不是魔杖……)

    (更不用说让它不可变了(通过代码,或者只是选择不改变它,这通常是使其成为线程安全的最简单的方法) )

    【讨论】:

    • +1 来自我。我添加了一个答案来涵盖另一种看待它的方式,但实际上只是因为它太长而无法放入对此答案的评论。
    • 感谢您提出这个建议。我的意思是原子性。但是,在我的具体例子中,int? (Nullable),这是否算作提到的引用类型,因此在访问原子性的意义上是线程安全的?
    • Nullable 是至少 40 位的值类型(由于填充可能更多),因此它不是自动原子的。
    • @Marcel - 正如@Jon 所指出的那样;不,那不会保证是原子的。
    • 补充:这是 Eric Lippert 对 Nullable 的具体回答:stackoverflow.com/questions/3047280/is-int-thread-safe/…
    【解决方案2】:

    我在这里回答是为了补充 Marc 的回答,他说“根据访问配置文件,还有很多不同的方法可以使某些东西成为线程安全的”。

    我只是想补充一下,部分原因是有很多方法不是线程安全的,当我们说某事是线程安全的时,我们有明确提供了哪些安全性。

    对于几乎所有可变对象,都会有处理它的方法不是线程安全的(注意几乎任何,都会出现异常)。考虑一个具有以下(线程安全)成员的线程安全队列;入队操作、出队操作和计数属性。通过对每个成员进行内部锁定,或者甚至使用无锁技术来构建其中一个相对容易。

    但是,假设我们这样使用对象:

    if(queue.Count != 0)
        return queue.Dequeue();
    

    上面的代码不是线程安全的,因为不能保证在(线程安全的)Count 返回 1 之后,另一个线程不会出队,从而导致第二次操作失败。

    在许多方面它仍然是一个线程安全的对象,特别是即使在这种失败的情况下,失败的出队操作也不会使对象进入无效状态。

    为了使一个对象在面对任何给定的操作组合时作为一个整体线程安全,我们必须使其在逻辑上不可变(它可能具有内部可变性,线程安全操作更新内部状态作为优化 - 例如通过记忆或根据需要从数据源加载,但在外部它必须看起来是不可变的)或严重减少可能的外部操作数量(我们可以创建一个只有EnqueueTryDequeue的线程安全队列始终是线程安全的,但这既减少了可能的操作,又强制将失败的出队重新定义为不是失败的,并强制更改从我们之前的版本调用代码的逻辑)。

    任何其他都是部分保证。我们免费获得了一些部分保证(正如 Marc 所指出的,就单个原子而言,对某些自动属性进行操作已经是线程安全的 - 在某些情况下,这就是我们需要的所有线程安全性,但在其他情况下不会去任何地方足够近了)。

    让我们考虑一个属性,它将这种部分保证添加到我们尚未获得它的那些情况。它对我们有多大价值?好吧,在某些情况下它会是完美的,但在其他情况下则不会。回到我们在出队之前进行测试的情况,在Count 上有这样的保证并没有多大用处——我们有这个保证,并且代码在多线程条件下仍然失败,而在单线程条件下则不会.

    更重要的是,将这种保证添加到还没有它的情况下至少需要一定程度的开销。一直担心开销可能是过早的优化,但是增加开销却没有收益是过早的悲观,所以我们不要这样做!更重要的是,如果我们确实提供更广泛的并发控制以使一组操作真正线程安全,那么我们将使更窄的并发控制变得无关紧要,它们变成纯粹的开销 - 所以我们甚至无法从我们的某些情况下的开销;这几乎总是纯粹的浪费。

    也不清楚并发问题的范围有多大。我们是否只需要锁定(或类似)该属性,还是需要锁定所有属性?我们是否还需要锁定非自动操作,这可能吗?

    这里没有好的单一答案(在推出您自己的解决方案时,它们可能是难以回答的问题,更不用说尝试在其他人使用此 [Threadsafe] 属性时会生成此类代码的代码中回答它) .

    此外,任何给定的方法都会有一组不同的条件,在这些条件下可能会发生死锁、活锁和类似问题,因此我们实际上可以通过将线程安全视为我们可以盲目应用于属性的东西来降低线程安全.

    如果无法找到这些问题的单一通用答案,就没有提供单一通用实现的好方法,并且任何此类 [Threadsafe] 属性充其量只能具有非常有限的价值。最后,在使用它的程序员的心理层面上,很可能会导致一种错误的安全感,即他们已经创建了一个线程安全的类,而实际上他们并没有;这实际上会使它变得比无用更糟。

    【讨论】:

    • 感谢您的详尽阐述。您对定义需要哪种线程安全的需求是正确的。就我而言,遗憾的是,问题中没有明确提及,我只是在寻求访问原子性。
    • 嗯,这可能是支持 [Atomic] 的论据,但是,您可以自己提供,而其他人需要走得更远,而第三人不会在意,并且再次第四次。有这么多不同的可能要求,我认为很少有人支持这一点,因为在最简单的情况下本质上是语法糖——一旦你想要更多,就做更多。
    【解决方案3】:

    不,不可能。这里没有免费的午餐。当您的自动属性需要更多提示(线程安全,INotifyPropertyChanged)时,您需要自己手动操作 - 没有自动属性魔法。

    【讨论】:

      【解决方案4】:

      根据 C# 4.0 规范,此行为没有改变:

      第 10.7.3 节 自动实现的属性

      当一个属性被指定为一个自动实现的属性时,一个隐藏的支持字段自动可用于该属性,并且访问器被实现为读取和写入该支持字段。

      下面的例子:

       public class Point {
          public int X { get; set; } // automatically implemented
          public int Y { get; set; } // automatically implemented
      }
      

      等价于以下声明:

      public class Point {
          private int x;
          private int y;
          public int X { get { return x; } set { x = value; } }
          public int Y { get { return y; } set { y = value; } }
      }
      

      【讨论】:

      • @BrokenGlass:你在回答另一个问题。问题是关于线程安全的,为什么要对自动实现的属性如何工作进行长篇大论?
      • 因为您可以看到自动属性的扩展方式在 C# 4.0 中没有改变,并且没有使用锁,因此这回答了这个问题。
      • 但这并不能证明没有改变行为的属性。
      • @BrokenGlass:实际上,它没有。他在问是否有一个属性或什么东西可以改变这个默认行为。
      • 你当然没问题,这段摘录并不能证明没有添加任何新内容,所以我坚持纠正。
      猜你喜欢
      • 2014-04-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多