我有一个ushort计数器(偶尔会翻身)。使用此值的消息传递协议不允许使用0.我需要一些线程安全的方法来在每次读取时递增此计数器(存储在类字段中),如果我将其存储为int并使用Interlocked.Increment。但是,我不确定如何将跳过0纳入其中。如果我偶尔会跳过几个数字,那也没关系;我的输出序列不一定非常完美。我不能在4000的任何块中重复使用相同的数字。我想避免使用锁。
答案 0 :(得分:4)
这一个:
假设:
static int value = ushort.MaxValue;
在代码中:
int temp, temp2;
do
{
temp = value;
temp2 = temp == ushort.MaxValue ? 1 : temp + 1;
}
while (Interlocked.CompareExchange(ref value, temp2, temp) != temp);
您必须使用int
然后将其投射(例如在get
属性中),因为Interlocked
不适用于所有基本类型。
我们可以在这样的高度线程化的上下文中使它快一点:
int temp = value;
while (true)
{
int temp2 = temp == ushort.MaxValue ? 1 : temp + 1;
int temp3 = Interlocked.CompareExchange(ref value, temp2, temp);
if (temp3 == temp)
{
break;
}
temp = temp3;
}
通过这种方式,我们不得不在失败时少读一遍。
正如我在评论中所写,此代码的核心思想是在计数器的临时变量(temp2
)中递增,然后尝试将我们知道的旧值与新值交换(Interlocked.CompareExchange
)。如果没有人触及中间的旧值(Interlocked.CompareExchange() == temp
),那么我们就完成了。如果其他人增加了值,那么我们再试一次。通过使用具有固定最大值(ushort
)的int
来模拟temp == ushort.MaxValue ? 1 : temp + 1
。
第二个版本,在Interlocked.CompareExchange()
失败时,将函数读取的值重新用作添加1的新基础。
以这种方式使用的Interlocked.CompareExchange
可以用作构建其他Interlocked
操作的基础(您需要Interlocked.Multiply
吗?您执行“标准”乘法,然后尝试{ {1}}旧值)