对于争用最少的轮循机制线程方案,我应该如何递增一个数字

本文关键字:我应该 方案 何递增 一个 线程 数字 争用 于争用 机制 | 更新日期: 2023-09-27 18:30:23

如果许多线程使用以下代码同时调用GetNextNumber,则GetNextNumber将比任何其他数字多返回 1 次。

private class RoundRobbinNumber
{
    private int _maxNumbers = 10;
    private int _lastNumber;
    private RoundRobbinNumber(int maxNumbers)
    {
        _maxNumbers = maxNumbers;
    }
    public int GetNextNumber()
    {
        int nextNumber = Interlocked.Increment(ref _lastNumber);
        if (_lastNumber > _maxNumbers)
        {
            Interlocked.CompareExchange(ref _lastNumber, 1, _maxNumbers);
            nextNumber = 1;
        }
        return nextNumber;
    }
}

有没有办法将_lastNumber重置回 1,并可靠地为每个调用 GetNextNumber() 的线程返回一个递增的数字,而不必使用锁?

对于争用最少的轮循机制线程方案,我应该如何递增一个数字

Andrey 没有条件语句的答案:

using System;
namespace Utils
{
    public class RoundRobinCounter
    {
        private int _max;
        private int _currentNumber = 0;
        public RoundRobinCounter(int max)
        {
            _max = max;
        }
        public int GetNext()
        {
            uint nextNumber = unchecked((uint)System.Threading.Interlocked.Increment(ref _currentNumber));
            int result = (int)(nextNumber % _max);
            return result;
        }
    }
}

这是一个运行此代码的 .net 小提琴。

诀窍是在循环中执行操作,直到成功。我在这里的回答中为这种方法提供了一个通用模板。

public int GetNextNumber()
{
  int initial, computed;
  do
  {
    initial = _lastNumber;
    computed = initial + 1;
    computed = computed > _maxNumbers ? computed = 1 : computed;
  } 
  while (Interlocked.CompareExchange(ref _lastNumber, computed, initial) != initial);
  return computed;
}

不确定是否对任何人有帮助,但这可能更简单:

class RoundRobinNumber
{
    private int _maxNumbers = 10;
    private int _lastNumber = 0;
    public RoundRobinNumber(int maxNumbers)
    {
        _maxNumbers = maxNumbers;
    }
    public int GetNextNumber()
    {
        int nextNumber = Interlocked.Increment(ref _lastNumber);
        int result = nextNumber % _maxNumbers;  
        return result >= 0 ? result : -result;
    }
}

通常使用循环从集合中选择项目。根据亚历克斯的回答,我做了一个RoundRobinCollection变体。

public class RoundRobinCollection<T>
{
    private readonly ReadOnlyCollection<T> _collection;
    private int _currentNumber = -1;
    public RoundRobinCollection(IEnumerable<T> enumerable)
    {
        _collection = new List<T>(enumerable).AsReadOnly();
        if (!_collection.Any())
        {
            throw new InvalidOperationException("Cannot use empty collection for RoundRobinCollection.");
        }
    }
    public T GetNext()
    {
        var index = GetNextIndex();
        return _collection[index];
    }
    private int GetNextIndex()
    {
        // This increments the currentNumber in a Thread-safe way, and deals with exceeding int.MaxValue properly
        var nextNumber = unchecked((uint)Interlocked.Increment(ref _currentNumber));
        return (int)(nextNumber % _collection.Count);
    }
}

使用示例:

public class SomeClient
{
    private readonly RoundRobinCollection<ServerConfig> _serverConfigs;
    public SomeClient(List<ServerConfig> serverConfigs)
    {
        _serverConfigs = new RoundRobinCollection<ServerConfig>(serverConfigs);
    }
    public void DoSomething(){
       var serverConfig = _serverConfigs.GetNext();
       // Do something with current serverConfig
    }
}