如何轻松使这个计数器属性线程安全?

我在类中有属性定义,我只有计数器,这必须是线程安全的,这不是因为getset不在同一个锁中,怎么做?

  private int _DoneCounter; public int DoneCounter { get { return _DoneCounter; } set { lock (sync) { _DoneCounter = value; } } } 

如果您希望以一种DoneCounter = DoneCounter + 1保证不受竞争条件限制的方式实现该属性,则无法在该属性的实现中完成。 该操作不是primefaces操作,实际上有三个不同的步骤:

  1. 检索DoneCounter的值。
  2. 加1
  3. 将结果存储在DoneCounter

您必须防止在任何这些步骤之间发生上下文切换的可能性。 锁定getter或setter内部将无济于事,因为该锁的范围完全存在于其中一个步骤(1或3)中。 如果要确保所有三个步骤一起发生而不被中断,那么您的同步必须涵盖所有三个步骤。 这意味着它必须在包含所有这三个的上下文中发生。 这可能最终会成为不属于包含DoneCounter属性的任何类的DoneCounter

使用您的物体的人负责照顾线程安全。 通常,没有具有读/写字段或属性的类可以以这种方式变为“线程安全的”。 但是,如果您可以更改类的接口以便不需要setter,则可以使其更加线程安全。 例如,如果你知道DoneCounter只增加和减少,那么你可以重新实现它,如下所示:

 private int _doneCounter; public int DoneCounter { get { return _doneCounter; } } public int IncrementDoneCounter() { return Interlocked.Increment(ref _doneCounter); } public int DecrementDoneCounter() { return Interlocked.Decrement(ref _doneCounter); } 

使用Interlocked类提供primefaces操作,即固有线程安全,如本LinqPad示例所示:

 void Main() { var counters = new Counters(); counters.DoneCounter += 34; var val = counters.DoneCounter; val.Dump(); // 34 } public class Counters { int doneCounter = 0; public int DoneCounter { get { return Interlocked.CompareExchange(ref doneCounter, 0, 0); } set { Interlocked.Exchange(ref doneCounter, value); } } } 

如果您不仅期望某些线程偶尔会同时写入计数器,但是许multithreading将继续这样做,那么您希望有几个计数器,至少一个缓存线彼此分开,并且有不同的线程写入不同的计数器,当你需要计数时将它们相加。

这使得大多数线程彼此保持不同,从而阻止它们将每个其他值从内核中刷出,并且相互减慢速度。 (除非你能保证每个线程保持分离,否则你仍然需要互锁)。

对于绝大多数情况,你只需要确保偶尔的争用不会弄乱数值,在这种情况下,Sean U的答案在各方面都更好(像这样的条纹计数器对于无争议的使用来说速度较慢)。

你究竟想用柜台做什么? 锁对整数属性的影响并不大,因为整数的读写都是primefaces的,有或没有锁定。 锁可以获得的唯一好处是增加了内存屏障; 通过在读取或写入共享变量之前和之后使用Threading.Thread.MemoryBarrier() ,可以实现相同的效果。

我怀疑你真正的问题是你正在尝试做一些像“DoneCounter + = 1”这样的事情,即使有锁定,它也会执行以下事件序列:

  获得锁定
  获取_DoneCounter
  释放锁定
  将一个值添加到已读取的值
  获得锁定
  将_DoneCounter设置为计算值
  释放锁定

不是很有帮助,因为值可能会在get和set之间发生变化。 所需要的是一种在没有任何干预操作的情况下执行获取,计算和设置的方法。 有三种方法可以实现:

  1. 在整个操作过程中获取并保持锁定
  2. 使用Threading.Interlocked.Increment向_Counter添加值
  3. 使用Threading.Interlocked.CompareExchange循环更新_Counter

使用这些方法中的任何一种,都可以根据旧值计算_Counter的新值,使得写入的值保证基于写入时_Counter的值。

您可以将_DoneCounter变量声明为“volatile”,以使其成为线程安全的。 看到这个:

http://msdn.microsoft.com/en-us/library/x13ttww7%28v=vs.71%29.aspx