使用Interlocked.CompareExchange递增计数器,直到值 [英] Using Interlocked.CompareExchange to increment a counter until a value

查看:137
本文介绍了使用Interlocked.CompareExchange递增计数器,直到值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要,直到达到一个特定的次数递增计数器。我可以使用两个并行任务递增的数目。而不是使用一个锁,检查数量并没有达到最大允许值,然后递增的,我想用的以下方式互锁 .CompareExchange:

I need to increment a counter until it reaches a particular number. I can use two parallel task to increment the number. Instead of using a lock to check if the number has not reach the maximum allowed value and then incrementing, I thought using Interlocked.CompareExchange in the following manner:

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),比锁的方法:

This code is taking more to execute (for _max= 1,000,000) than the lock approach:

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的方式有问题,但我一直无法弄清楚。有没有办法不使用锁(又名互锁方法)执行上述逻辑更好的办法?

There might be a problem with the way I am using Interlocked.CompareExchange but I have not been able to figure out. Is there a better way to perform the above logic without using lock (aka Interlocked methods)?



更新
结果
我能来与它执行为有锁版一样好一个版本(迭代= 1,000,000,更好的为> 1,000,000迭代)。


Update
I was able to come with a version which performs as good as the lock version (for iterations = 1,000,000 and better for > 1,000,000 iterations).

    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契机,进一步进步,而不是执行一个繁忙的自旋等待。


The difference is made by the spin. If the task is unable to increment the variable on first go it spins providing an opportunity for task 2 to progress further instead of performing a busy spin wait.

我怀疑锁定漂亮很多不一样的,它可能采用的策略旋转,并允许线程当前持有锁来执行。

I suspect lock pretty much does the same, it could employ a strategy to spin and allow the thread currently owning the lock to execute.

推荐答案

问题这里要说的是,你实际上是做了很多工作互锁版本 - 我指的是更多的迭代。这是因为, 很多的时间 CompareExchange 没有做任何事情的,因为该值是由其他线程改变。您可以通过共增加每个回路看到这一点:

The problem here is that you are actually doing a lot more work in the Interlocked version - by which I mean more iterations. This is because a lot of the time the CompareExchange isn't doing anything, because the value was changed by the other thread. You can see this by adding a total to each loop:

    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 不是保存在寄存器中)

(note I also added a VolatileRead to ensure _counter isn't held in a register)

我得到的超过迭代(通过),你可能期望在这里。问题的关键是,当使用互锁这样,你需要添加一个策略,如果该值改变,即重试策略会发生什么。

I get much more than iterations (via total) that you might expect here. The point is that when using Interlocked in this way, you need to add a strategy for what happens if the value changed, i.e. a retry strategy.

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

For example, a crude retry strategy might be:

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



这是说:不断重试,直到你把它的工作 - 任何做代码只会发生的之后的那张支票(其中总++ 线是目前)。 。然而,这使得代码更昂贵

which is to say: keep retrying until you make it work - any "doing" code would only happen after that check (where the total++ line is currently). This, however, makes the code more expensive.

如果锁定便宜:使用锁定。这没有什么错锁定的,而事实上这是非常内部优化。无锁是不会自动等同于最快或确实是简单。

If lock is cheaper: use lock. There's nothing wrong with lock, and indeed it is very optimized internally. Lock-free is not automatically the same as "fastest" or indeed "simplest".

这篇关于使用Interlocked.CompareExchange递增计数器,直到值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆