最佳的线程安全方式将整数递增至65535。

13
我有一个System.Timers.Timer,每3秒增加一次计数器。在某些条件下,另一个线程可能会将此变量设置为任何值。
尝试使用Interlocked.Increment,但它没有UInt16的重载。接下来想到的是锁(lock),但我不确定如何对该变量进行线程安全的访问(读/写/增量)。
编辑:代码最初使用int,但根据建议更改为UInt16。
private volatile System.UInt16 mCounter = 0;
private readonly object mCounterLock = new object();
public System.UInt16 Counter {
  get {
    lock (mCounterLock) {
      return mCounter;
    }
  }
  set {
    lock (mCounterLock) {
      mCounter = value;
    }
  }
}
private System.Timers.Timer mCounterTimer;

void mCounter_Elapsed(object sender, System.Timers.ElapsedEventArgs e) {
  lock (mCounterLock) {
    Counter++;
  }
}

1
@JesusRamos,但是没有Interlocked.Increment(Int16)。 - fxam
1
告诉我们要求会更容易,而不是只告诉我们一半的解决方案。从您最初的问题和编辑中可以看出,这个问题与递增int或uint16无关,而是将某种类型递增到65535然后重新开始。如果您不讨论特定递增“int”或“UInt16”并添加不必要的限制,那么回答起来可能会更容易。 - jalf
1
@fxam:但根据您最初的问题,它不一定是uint16。如果有人为您提供使用另一种数据类型的解决方案怎么办?在您最初的问题中,并没有要求数据类型是uint16。但现在是吗?这有意义吗? - jalf
如果您不喜欢我的编辑,随时可以撤销它。 - jalf
@jalf,原始问题是我只需要从Int32中获取2个字节。JesusRamos指出它完美地适合UInt16并自然溢出。话虽如此,现在我认为你也有一点道理:如果其他人有一个不使用UInt16的解决方案呢? - fxam
显示剩余2条评论
4个回答

8

使用 Interlocked.CompareExchangeInterlocked.Increment 的组合,在值达到65535时将其赋值为0。


7

如果您只需要2个字节,请将Int32值更改为Int16。以下是一些代码,因为Shai已经删除了他的答案:

UInt16 myval = 0;
Object myvalLock = new Object();
....
lock (myvalLock) { myval++; }

7

我只会使用 Interlocked.Increment 中的 UInt32,并且在每次读取访问之后将其转换为 UInt16


1
volatile int iNum = 0;
...


iActual = iNum;
do
{
   iExpected = iActual;
   iNext = (iExpected+1) & 0xFFFF;
   iActual = Interlocked.CompareExchange (ref iNum, iNext, iExpected);
} while (iExpected != iActual);
return iNext;

这使得递增线程相对于其他递增操作是安全的。但您还提到了“读取”、“写入”和“重置”,无法确定在上下文中这些操作是否安全,甚至是否递增相对于所说的“写入”和特别是“重置”操作是安全的。通常,对于这种共享计数器,唯一允许的操作是递增它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接