多线程增量和跳过 0 没有锁?

Posted

技术标签:

【中文标题】多线程增量和跳过 0 没有锁?【英文标题】:multi-threaded increment and skip 0 without a lock? 【发布时间】:2013-09-03 19:38:09 【问题描述】:

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

【问题讨论】:

你能用do result = Interlocked.Increment(ref value); while (result==0); 来避免0吗? 我认为只要在 while 检查中将结果转换为 ushort 就可以了。似乎在碰撞情况下它只会跳过 1. @MarcGravell 其他一些线程可以读取Interlocked.Increments 之间的 0 值。 @xanatos 所以?没关系 - 因为新数字之后的所有代码都会运行相同的访问器 - 所以没有人会得到零 @MarcGravell 您可以让纯读者在阅读 0 并“伪造它”时返回 1……(他们不知道它是否已经是 1……但他们知道它将变成 1如果它是 0,则在几秒钟内) 【参考方案1】:

这个:

给定:

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),然后尝试用新值 (@ 987654328@)。如果没有人触及中间的旧值(Interlocked.CompareExchange() == temp),那么我们就完成了。如果其他人增加了该值,那么我们再试一次。 ushort 是通过使用具有固定最大值 (temp == ushort.MaxValue ? 1 : temp + 1) 的 int 来模拟的。

第二个版本,在 Interlocked.CompareExchange() 失败时重用函数读取的值作为添加 1 的新基础。

以这种方式使用的Interlocked.CompareExchange 可以用作构建其他Interlocked 操作的基础(你想要Interlocked.Multiply?你做一个“标准”乘法然后尝试Interlocked.CompareExchange 旧值)

【讨论】:

我仍然需要一个 ushort 结果。我不确定这是否检测到超短翻转。 这对我来说有点过分。我认为当从多个线程同时调用时这会中断。 @spender 可以使用int 作为支持字段。修改了代码。 @spender 第一次读取 (temp = value;) 可能是脏的,但如果它脏了,Interlocked.CompareExchange 将失败并强制“真正”读取该值。然后重新读取该值,但此时我们与其他线程“同步”。如果两个线程同时进入该代码块,则只有一个Interlocked.CompareExchange会成功,而另一个会失败。第二个版本对失败的读取减少了(我们重用了Interlocked.CompareExchange读取) +1:这绝对是正确的答案。对于那些不知道的人,最上面的例子是创建复杂操作的类似联锁变体的基本模板。我已经多次这样做并且效果很好。这是一个久经考验的模式。

以上是关于多线程增量和跳过 0 没有锁?的主要内容,如果未能解决你的问题,请参考以下文章

.net 多线程 lock锁的问题

6.23Java多线程可重入锁实现原理

python 多线程-01 锁

多线程互斥锁读写锁

python 多线程锁机制

干货 | Java多线程-各种线程锁