我有一个非常简单的操作,需要以原子方式完成:
if (a > b)
b = a
其中a和b是整数
编辑:,a是本地的。
在C#中有快速的方法吗?如果可能的话,我想避免手动锁定。我看过Interlocked.CompareExchange,但据我了解,这只是测试是否相等。
谢谢!
答案 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
是一个局部变量,或者至少不受异步写入的影响。 a
和b
都可以在你的背后修改,然后没有无锁的方式以原子方式进行这种更新。 (感谢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
是本地的,因此a
和b
可能会被其他人访问螺纹object lockObject = new object();
int a = 10;
int b = 5;
lock (lockObject)
{
if (a > b)
{
b = a
}
}
答案 3 :(得分:0)
这些是我在阅读了其他答案后得出的一些优化的“食谱”。与问题没有直接关系,但请在此处添加,因为这是相关搜索的位置。尝试将其保留为a > b
或a >= b
以匹配原始问题。并使其具有通用性,以免使描述偏离其他相关问题。两者都可以包装成方法。
b
如果这是对b
或b
执行的 only操作,否则单调递增,则可以优化互锁分配和重试,其中a > b == false
:
int initial;
do
{
initial = b;
}
while ((a > initial) && Interlocked.CompareExchange(ref b, a, initial) != initial);
如果a > b == false
在任何重试之后都不会变为true
(b
只会变大),并且可以跳过交换,因为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"),这是相关但明显的问题,那么这也不起作用。