在比较结果上的原子交换价值

时间:2011-08-17 22:13:36

标签: c# .net multithreading synchronization atomic

我有一个非常简单的操作,需要以原子方式完成:

if (a > b)
  b = a

其中a和b是整数

编辑:,a是本地的。

在C#中有快速的方法吗?如果可能的话,我想避免手动锁定。我看过Interlocked.CompareExchange,但据我了解,这只是测试是否相等。

谢谢!

4 个答案:

答案 0 :(得分:8)

规范的方法是在循环中使用互锁比较交换:

int oldvalue, newvalue ;
do {
  oldvalue = b ; // you'll want to force this to be a volatile read somehow
  if( a > oldvalue )
    newvalue = a ;
  else
    break ;
} while( interlocked replace oldvalue with newvalue in b does NOT succeed );

(伪代码因为我不打算在C#中查找正确的互锁交换方式。)

如您所见,除非您有高效的问​​题,否则使用普通的互斥锁会更简单,更易读。

编辑:这假定a是一个局部变量,或者至少不受异步写入的影响。 ab都可以在你的背后修改,然后没有无锁的方式以原子方式进行这种更新。 (感谢silev指出这一点。)

答案 1 :(得分:4)

Henning is correct。我将提供与C#相关的详细信息。可以使用以下函数推广该模式。

public static T InterlockedOperation<T>(ref T location, T operand)
{
  T initial, computed;
  do
  {
    initial = location;
    computed = op(initial, operand); // where op is replaced with a specific implementation
  } 
  while (Interlocked.CompareExchange(ref location, computed, initial) != initial);
  return computed;
}

在您的具体情况下,我们可以像这样定义InterlockedGreaterThanExchange函数。

public static int InterlockedGreaterThanExchange(ref int location, int value)
{
  int initial, computed;
  do
  {
    initial = location;
    computed = value > initial ? value : initial;
  } 
  while (Interlocked.CompareExchange(ref location, computed, initial) != initial);
  return computed;
}

答案 2 :(得分:0)

没有这样的原子操作以真正的原子方式执行比较和赋值。在C#中,真正的原子操作可以是Interlocked操作,如CompareExchenge()Exchange(),以交换两个整数值(32位为32位架构,64位为64位处理器架构)。

显然简单的赋值a = b是两个操作 - 读写。因此,没有可能在一个原子步骤中进行比较+赋值...在任何其他语言/架构中是否可能,我的意思是真正的原子? ;)

编辑:原始问题没有表明'a'是局部变量...呃呃这么小的修正......任何方式我都会留下我的答案,因为我相信真正的原子操作的性质而不是某种有野心的“诡计”

总结一下:

  • 我们不能做单原子操作等操作
  • 只有一种方法是使用锁定机制同步它(假设原始问题没有说明a是本地的,因此ab可能会被其他人访问螺纹
object lockObject = new object();
int a = 10;
int b = 5;

lock (lockObject)
{
   if (a > b)
   {
      b = a
   }
}

答案 3 :(得分:0)

这些是我在阅读了其他答案后得出的一些优化的“食谱”。与问题没有直接关系,但请在此处添加,因为这是相关搜索的位置。尝试将其保留为a > ba >= b以匹配原始问题。并使其具有通用性,以免使描述偏离其他相关问题。两者都可以包装成方法。

优化-单调b

如果这是对bb执行的 only操作,否则单调递增,则可以优化互锁分配和重试,其中a > b == false

int initial;
do 
{
  initial = b;
}
while ((a > initial) && Interlocked.CompareExchange(ref b, a, initial) != initial);

如果a > b == false在任何重试之后都不会变为trueb只会变大),并且可以跳过交换,因为b不会受到影响。< / p>

相关问题优化:节流不畅

自局部变量signal()(例如,系统时间样本)每增加一个常量a后,每次

操作threshold >= 1仅应一次上次调用signal()的时间。与b相比,a是阈值占位符,如果a + threshold则设置为a >= b。只有“获胜”线程应该调用signal()

var initial = b;
if ((a > initial) && Interlocked.CompareExchange(ref b, a + threshold, initial) == initial)
{
  signal();
}

这里,b还是单调的,但是现在我们可以完全优化重试循环,因为我们只想知道本地线程是否在尊重threshold的情况下与其他线程“竞争了”。这种方法将有效地“阻止”任何其他线程在已定义的阈值范围内发信号。

节流警告

如果您需要“严格的”节流阀(即,“最小”的a,其中a >= b –因此标题中的“松散”),则此方法将不起作用。如果您只需要报告一系列紧密分组的a中的最后一个(也可能称为"debouncing"),这是相关但明显的问题,那么这也不起作用。