在.NET中使用Interlocked类实现位运算

8

我正在尝试在多线程的.NET应用程序中设置共享变量的位标志,但是找不到与本地InterlockedOr函数相对应的托管Interlocked类。我想出了以下代码来执行|=赋值,但理论上可能会出现无限循环的情况,这让我感到不安:

long currentValue;
long updatedValue;

do
{
    // Spin until successful update. Value must be read using Interlocked.Read()
    // to be truly atomic on 32 bit systems (see MSDN).
    currentFlags = Interlocked.Read(ref _currentFlags);
    updatedValue = currentFlags | flag;
} while (currentFlags != Interlocked.CompareExchange(ref _currentFlags, updatedValue, currentFlags));

是否可以只使用 Interlocked 类中的函数以更安全的方式实现这个功能?如果可能的话,我希望避免涉及显式锁定的解决方案。


1
虽然在使用CompareExchange时存在无限循环的可能性,但这种情况极少出现,可以忽略不计。.NET框架广泛使用此技术,包括在所有并发集合中,以实现无锁同步。 - Paul Turner
只有在其他线程并发地将标志重置为零,并且足够灵活以每次都击败您的线程进行无限次尝试时,您才会得到一个无限循环。如果发生这种情况,您需要解决更大的问题,找出“敏捷”线程为什么要做它所做的事情。我认为您的解决方案是使用Interlocked最好的方法。 - Sergey Kalinichenko
1个回答

1
假设(这些限制并非根本性质,只是为了简化说明):
  • 在开始时,currentFlags0
  • 我们最多在一步中设置一个标志,
  • 我们不会触及符号位。
请注意,如果我们将位 kcurrentFlags 中设置为 1 一次,我们可以将 or 替换为 + 1L << k。因此,我们可以使用辅助数组 set 来记住哪些位已经设置,并在需要时进行 Interlocked.Add
long currentFlags = 0;
int[] set = new int[sizeof(long) * 8];

....

int k = ...; // bit to set
if(Interlocked.Exchange(ref set[k], 1) == 0)
    Interlocked.Add(ref currentFlags, 1L << k);

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