gpt4 book ai didi

c# - 使用 Interlocked.CompareExchange 递增计数器直到一个值

转载 作者:行者123 更新时间:2023-11-30 12:11:25 29 4
gpt4 key购买 nike

我需要递增一个计数器直到它达到一个特定的数字。我可以使用两个并行任务来增加数字。我没有使用锁来检查数字是否未达到最大允许值然后递增,而是使用 Interlocked .CompareExchange 以下列方式:

public class CompareExchangeStrategy
{
private int _counter = 0;
private int _max;

public CompareExchangeStrategy(int max)
{
_max = max;
}

public void Increment()
{
Task task1 = new Task(new Action(DoWork));
Task task2 = new Task(new Action(DoWork));
task1.Start();
task2.Start();
Task[] tasks = new Task[2] { task1, task2 };
Task.WaitAll(tasks);

}

private void DoWork()
{
while (true)
{
int initial = _counter;
if (initial >= _max)
{
break;
}
int computed = initial + 1;
Interlocked.CompareExchange(ref _counter, computed, initial);
}
}

}

这段代码比锁方法花费更多的时间来执行(对于 _max= 1,000,000):

public class LockStrategy
{
private int _counter = 0;
private int _max;

public LockStrategy(int max)
{
_max = max;
}

public void Increment()
{
Task task1 = new Task(new Action(DoWork));
Task task2 = new Task(new Action(DoWork));
task1.Start();
task2.Start();
Task[] tasks = new Task[2] { task1, task2 };
Task.WaitAll(tasks);

}

private void DoWork()
{
while (true)
{
lock (_lockObject)
{
if (_counter < _max)
{
_counter++;
}
else
{
break;
}
}
}
}

}

我使用 Interlocked.CompareExchange 的方式可能有问题,但我无法弄清楚。有没有更好的方法来执行上述逻辑而不使用锁(又名互锁方法)?


更新
我能够提供一个性能与锁定版本一样好的版本(对于迭代 = 1,000,000,对于 > 1,000,000 迭代更好)。

    SpinWait spinwait = new SpinWait();
int lock =0;
while(true)
{

if (Interlocked.CompareExchange(ref lock, 1, 0) != 1)
{

if (_counter < _max)
{
_counter++;
Interlocked.Exchange(ref lock, 0);
}
else
{
Interlocked.Exchange(ref lock, 0);
break;
}

}
else
{
spinwait.SpinOnce();
}
}


区别在于旋转。如果任务无法在第一次执行时增加变量,它会自旋,为任务 2 提供进一步推进的机会,而不是执行繁忙的自旋等待。

我怀疑 lock 几乎做同样的事情,它可以采用自旋策略并允许当前拥有锁的线程执行。

最佳答案

这里的问题是您实际上在 Interlocked 版本中做了更多的工作——我的意思是更多的迭代。这是因为 很多 CompareExchange 没有做任何事情,因为值被改变了另一个线程。您可以通过向每个循环添加总计来看到这一点:

    int total = 0;
while (true)
{
int initial = Thread.VolatileRead(ref _counter);
if (initial >= _max)
{
break;
}
int computed = initial + 1;
Interlocked.CompareExchange(ref _counter, computed, initial);
total++;
}
Console.WriteLine(total);

(请注意,我还添加了一个 VolatileRead 以确保 _counter 未保存在寄存器中)

我得到的迭代次数(通过 total)比您在这里可能期望的要多。关键是当以这种方式使用 Interlocked 时,您需要添加一个策略来处理值更改时发生的情况,即重试策略。

例如,粗略的重试策略可能是:

    while (true)
{
int initial = Thread.VolatileRead(ref _counter);
if (initial >= _max)
{
break;
}
int computed = initial + 1;
if (Interlocked.CompareExchange(ref _counter, computed, initial)
!= initial) continue;
total++;
}

也就是说:不断重试,直到你让它工作 - 任何“执行”代码只会在检查之后发生(当前 total++ 行所在的位置)。然而,这会使代码更加昂贵。

如果 lock 更便宜:使用 locklock 没有什么问题,而且确实在内部进行了非常优化。无锁并不自动等同于“最快”或“最简单”。

关于c# - 使用 Interlocked.CompareExchange 递增计数器直到一个值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15495459/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com