多线程增量并跳过没有锁定的0?

时间:2013-09-03 19:38:09

标签: c# .net multithreading atomic

我有一个ushort计数器(偶尔会翻身)。使用此值的消息传递协议不允许使用0.我需要一些线程安全的方法来在每次读取时递增此计数器(存储在类字段中),如果我将其存储为int并使用Interlocked.Increment。但是,我不确定如何将跳过0纳入其中。如果我偶尔会跳过几个数字,那也没关系;我的输出序列不一定非常完美。我不能在4000的任何块中重复使用相同的数字。我想避免使用锁。

1 个答案:

答案 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}}旧值)