根据比较结果自动交换值

发布于 2024-11-30 01:10:08 字数 240 浏览 0 评论 0原文

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

if (a > b)
  b = a

其中 a 和 b 是整数

编辑:,a 是本地的。

在 C# 中是否有快速的方法来做到这一点?如果可能的话,我想避免手动锁定。我查看了 Interlocked.CompareExchange,但据我了解,这仅测试相等性。

谢谢!

I have a very simple operation that needs to be done atomically:

if (a > b)
  b = a

where a and b are ints

EDIT: and a is local.

Is there a fast way to do this in C#? I'd like to avoid locking manually if possible. I've looked at Interlocked.CompareExchange, but as I understand it, this only tests for equality.

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

围归者 2024-12-07 01:10:08

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

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 指出这一点)。

The canonical way is to use interlocked compare-exchange in a loop:

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 );

(Pseudocode because I don't bother to look up the correct way to do an interlocked exchange in C#).

As you see, unless you have overriding efficiency concerns, using plain old mutexes is far simpler and more readable.

Edit: This assumes that a is a local variable or at least not subject to asynchronous writes. It both of a and b can be modified behind your back, then there is no lock-free way of doing this update atomically. (Thanks to silev for pointing this out).

烟雨凡馨 2024-12-07 01:10:08

亨宁是正确的。我将提供与 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;
}

Henning is correct. I will provide the details as they pertain to C#. The pattern can be generalized with the following function.

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;
}

In your specific case we can define an InterlockedGreaterThanExchange function like this.

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;
}
拍不死你 2024-12-07 01:10:08

不存在这样的原子操作来以真正的原子方式执行比较和赋值。在 C# 中,真正的原子操作可以是Interlocked 操作,例如 CompareExhenge()Exchange() 来交换两个整数值(32 位架构为 32 位,64 位架构为 64 位)。 -bit 用于 64 位处理器架构)。

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

编辑:原始问题并不表明“a”是局部变量... uhhhggg 这样一个小修正...无论如何我都会保留我的答案,因为我相信真正的原子操作的本质,并且不是某种有野心成为原子的“技巧”

所以总结一下:

  • 我们不能将此类操作作为单个原子操作进行,
  • 只有一种方法是使用锁定机制来同步它(假设原始问题没有说明) a 是本地,因此 ab 都可能被其他线程访问
object lockObject = new object();
int a = 10;
int b = 5;

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

There is no such atomic operation to execute both comparision and assignment in true atomic manner. In C# true atomic operations could be Interlocked operations like CompareExchenge() and Exchange() to exchange two integer values (32bit for 32bit architecture and 64-bit for 64 bit processor architecture).

Obviously simple assignment a = b is a two operations - read and write. So there is no possibility to do comparision + assignment in one atomic step... Does it possible in any other language/architecture, I mean true atomic? ;)

EDIT: Original question does not indicated that 'a' is local variable... uhhhggg such a small correction... any way I'll leave my answer as is because I believe in nature of true atomic operations and not in some kind of 'tricks' which has ambitions to be atomic

So to summarize:

  • we can not do such operations as single atomic operation
  • only one way is to syncronize it using locking mechanism (assuming original question which does not states that a is local, so both a and b potentially could be accessed by an other thread
object lockObject = new object();
int a = 10;
int b = 5;

lock (lockObject)
{
   if (a > b)
   {
      b = a
   }
}
女皇必胜 2024-12-07 01:10:08

这些是我在阅读其他答案后想到的一些优化的“食谱”。与问题没有直接关系,但在此处添加,因为这是相关搜索的着陆点。试图将其保留为 a > ba >= b 以匹配原始问题。并保持它们的一般性,以免描述偏离其他相关问题。两者都可以包装到方法中。

优化 - 单调 b

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

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

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

相关问题优化:松散限制

动作signal()每次局部变量a(例如,系统时间样本)时只应调用一次自上次调用 signal() 以来,已经增加了某个常数 threshold >= 1。这里,b 是与 a 相比的阈值占位符,如果 a >= b 则设置为 a + Threshold 。只有“获胜”线程才应该调用 signal()

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

在这里,b 再次是单调的,但现在我们可以完全优化重试循环,因为我们只想知道本地线程是否在尊重阈值的同时与其他线程“赢得了比赛”代码>.此方法将有效地“阻止”任何其他线程在定义的阈值内发出信号。

节流注意事项

如果您需要“严格”节流(即“最小”a,其中 a >= b),则此选项将不起作用 - 因此“标题中的“宽松”。如果您只需要报告一系列紧密分组的 a 中的最后一个,这也不起作用 - 可能称为 “去抖动”,相关但不同的问题。

These are a couple of optimized "recipes" I came up with after reading other answers. Not directly related to the question, but adding here since this is where related searches landed. Tried to keep this to a > b or a >= b to match the original question. And to keep them general so as not to bias the descriptions away from other related problems. Both could be wrapped into methods.

Optimization - Monotonic b

If this is the only operation that is being done to b or b is otherwise monotonically increasing, you can optimize away the interlocked assignment and the retries where a > b == false:

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

If a > b == false it won't be true after any retry (b is only getting larger) and the exchange can be skipped since b would be unaffected.

Related problem optimization: Loose throttling

Action signal() should only be called once each time a local variable a (e.g., a system time sample) has increased by some constant threshold >= 1 since the last time signal() was called. Here, b is a threshold placeholder compared to a and set to a + threshold if a >= b. Only the "winning" thread should call signal().

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

Here, b is again monotonic, but now we can optimize away the retry loop entirely since we only want to know if the local thread "won the race" with other threads while respecting threshold. This method will effectively "block" any other thread from signalling within the defined threshold.

Throttling Caveats

This one will not work if you need a "strict" throttle--that is, the "smallest" a where a >= b--thus the "loose" in the title. This will also not work if you need to report only the last in a series of closely grouped as--what might be called "debouncing", related but distinct problem.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文