如何冻结.NET冰棒(使类不可变的) [英] How to freeze a popsicle in .NET (make a class immutable)

查看:150
本文介绍了如何冻结.NET冰棒(使类不可变的)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我设计一个类,我想只读使主线程完成配置后,即冻结了。埃里克利珀称这冰棒不变性。它被冻结后,它可以通过多个线程同时进行读取访问。

I'm designing a class that I wish to make readonly after a main thread is done configuring it, i.e. "freeze" it. Eric Lippert calls this popsicle immutability. After it is frozen, it can be accessed by multiple threads concurrently for reading.

我的问题是如何写这一个线程安全的方式,是的实事求是的效率,即没有试图成为不必要的巧妙

My question is how to write this in a thread safe way that is realistically efficient, i.e. without trying to be unnecessarily clever.

尝试1:

public class Foobar
{
   private Boolean _isFrozen;

   public void Freeze() { _isFrozen = true; }

   // Only intended to be called by main thread, so checks if class is frozen. If it is the operation is invalid.
   public void WriteValue(Object val)
   {
      if (_isFrozen)
         throw new InvalidOperationException();

      // write ...
   }

   public Object ReadSomething()
   {
      return it;
   }
}



埃里克利珀似乎在暗示这将是确定< A HREF =htt​​p://blogs.msdn.com/b/ericlippert/archive/2011/05/23/read-only-and-threadsafe-are-different.aspx>这个职位。
我知道写有释放的语义,但据我了解这仅适用于的排序的,它并不一定意味着所有的线程将看到写后立即值。任何人都可以证实这一点?这将意味着这个解决方案不是线程安全的(这可能不是当然的唯一原因)

Eric Lippert seems to suggest this would be OK in this post. I know writes have release semantics, but as far as I understand this only pertains to ordering, and it doesn't necessarily mean that all threads will see the value immediately after the write. Can anyone confirm this? This would mean this solution is not thread safe (this may not be the only reason of course).

尝试2:

以上,但使用 Interlocked.Exchange 来确保值实际上是出版:

The above, but using Interlocked.Exchange to ensure the value is actually published:

public class Foobar
{
   private Int32 _isFrozen;

   public void Freeze() { Interlocked.Exchange(ref _isFrozen, 1); }

   public void WriteValue(Object val)
   {
      if (_isFrozen == 1)
         throw new InvalidOperationException();

      // write ...
   }
}

在这里的优势是,我们保证值无痛苦在每次读取的开销出版。如果没有的读取写入到_isFrozen之前的互锁方法使用一个完整的内存屏障被移到我想这是线程安全的。但是,谁知道,编译器会做什么(根据这似乎是相当多的C#规范的第3.10节),所以我不知道这是否是线程安全的。

Advantage here would be that we ensure the value is published without suffering the overhead on every read. If none of the reads are moved before the write to _isFrozen as the Interlocked method uses a full memory barrier I would guess this is thread safe. However, who knows what the compiler will do (and according to section 3.10 of the C# spec that seems like quite a lot), so I don't know if this is threadsafe.

尝试3:

也可做使用互锁

public class Foobar
{
   private Int32 _isFrozen;

   public void Freeze() { Interlocked.Exchange(ref _isFrozen, 1); }

   public void WriteValue(Object val)
   {
      if (Interlocked.CompareExchange(ref _isFrozen, 0, 0) == 1)
         throw new InvalidOperationException();

      // write ...
   }
}

绝对线程安全的,但似乎有点浪费要做的每一个读比较交流。我知道这是开销大概很少,但我正在寻找一个的合理的有效方法(虽然也许这就是它)。

Definitely thread safe, but it seems a little wasteful to have to do the compare exchange for every read. I know this overhead is probably minimal, but I'm looking for a reasonably efficient method (although perhaps this is it).

尝试4:

使用挥发性

public class Foobar
{
   private volatile Boolean _isFrozen;

   public void Freeze() { _isFrozen = true; }

   public void WriteValue(Object val)
   {
      if (_isFrozen)
         throw new InvalidOperationException();

      // write ...
   }
}

但乔·达菲宣布再见挥发性,所以我不会考虑这究竟是一种解决方案。

But Joe Duffy declared "sayonara volatile", so I won't consider this a solution.

尝试5:

锁定一切,似乎有点矫枉过正:

Lock everything, seems a bit overkill:

public class Foobar
{
   private readonly Object _syncRoot = new Object();
   private Boolean _isFrozen;

   public void Freeze() { lock(_syncRoot) _isFrozen = true; }

   public void WriteValue(Object val)
   {
      lock(_syncRoot) // as above we could include an attempt that reads *without* this lock
         if (_isFrozen)
            throw new InvalidOperationException();

      // write ...
   }
}

似乎也绝对线程安全的,但比使用上述互锁方式更多的开销,所以我赞成尝试3在这一个。

Also seems definitely thread safe, but has more overhead than using the Interlocked approach above, so I would favour attempt 3 over this one.

然后我可以来与至少一些(我敢肯定还有更多):

And then I can come up with at least some more (I'm sure there are many more):

尝试6:使用发.VolatileWrite Thread.VolatileRead ,但这些都被认为是沉重的一面一点点。

Attempt 6: use Thread.VolatileWrite and Thread.VolatileRead, but these are supposedly a little on the heavy side.

尝试7:使用 Thread.MemoryBarrier ,似乎有点过于的内部

Attempt 7: use Thread.MemoryBarrier, seems a little too internal.

尝试8:创建一个不可改变的副本 - 不希望这样做。

Attempt 8: create an immutable copy - don't want to do this

总的来讲:


  • 你会使用哪些尝试和为什么(或者完全不同的你会怎么做)? (即什么是发布一个价值的最佳方式,一旦被然后读并行,而被无过于聪明的合理有效?)

  • 确实.NET的内存模型释放的语义写入意味着所有其他线程看到更新(高速缓存一致性等)?我一般不想考虑太多,但它是很好的有一个了解

编辑:

也许我的问题是不明确的,但我期待特别的原因,为什么上面的尝试是好是坏。请注意,我在这里谈论一个单一的作家的情况写入然后冻结任何并发读取之前。我相信尝试1是确定的,但我想知道为什么(我不知道是否可以读取不知何故被优化掉,例如)。
我不关心这是否是好的设计的做法,但更多关于它的实际线程方面。

Perhaps my question wasn't clear, but I am looking in particular for reasons as to why the above attempts are good or bad. Note that I am talking here about a scenario of one single writer that writes then freezes before any concurrent reads. I believe attempt 1 is OK but I'd like to know exactly why (as I wonder if reads could be optimized away somehow, for example). I care less about whether or not this is good design practice but more about the actual threading aspect of it.

为响应收到了许多感谢的问题,但我选择了,因为我觉得给的答案并不以纪念这个作为回答自己的非常的回答我的问题,我不希望给印象任何人访问该标记答案是正确的,只是因为它被自动标记为这类网站由于赏金到期。
此外,我不认为带票是压倒性的投票支持,不足以自动标记为一个答案最多的答案。

Many thanks for the response the question received, but I have chosen to mark this as an answer myself because I feel that the answers given do not quite answer my question and I do not want to give the impression to anyone visiting the site that the marked answer is correct simply because it was automatically marked as such due to the bounty expiring. Furthermore I do not think the answer with the highest number of votes was overwhelmingly voted for, not enough to mark it automatically as an answer.

我仍然倚尝试#1是正确的,但是,我也喜欢一些权威的答案。我理解的x86具有很强的模式,但我不想(也不应该)为一个特定的架构代码,毕竟这是关于.NET中的好东西之一。

I am still leaning to attempt #1 being correct, however, I would have liked some authoritative answers. I understand x86 has a strong model, but I don't want to (and shouldn't) code for a particular architecture, after all that's one of the nice things about .NET.

如果你是在对答案疑问,去锁定的方法之一,或许与这里显示的优化,避免了大量的锁争用。

If you are in doubt about the answer, go for one of the locking approaches, perhaps with the optimizations shown here to avoid a lot of contention on the lock.

推荐答案

也许略有话题,但只是出于好奇:)你为什么不使用真实的永恒?例如使冻结()返回一个不可改变的副本(没有写入方法或任何其他可能改变内部状态),并使用该副本而不是原始对象。你甚至可以去不改变状态,并在返回每次写操作的新副本(与改变的状态),而不是(据我所知串类的工作这一点)。 真正的不变性本质上是线程安全的。

Maybe slightly of topic but just out of curiosity :) Why don't you use "real" immutability? e.g. making Freeze() return an immutable copy (without "write methods" or any other possibility to change the inner state) and using this copy instead of the original object. You could even go without changing the state and return a new copy (with the changed state) on each write operation instead (afaik the string class works this). "Real immutability" is inherently thread safe.

这篇关于如何冻结.NET冰棒(使类不可变的)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆