Skip to content

Instantly share code, notes, and snippets.

@randyburden
Created November 12, 2021 03:43
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save randyburden/251f0514f893013d711da1b4e395aaa5 to your computer and use it in GitHub Desktop.
Save randyburden/251f0514f893013d711da1b4e395aaa5 to your computer and use it in GitHub Desktop.
C# Round Robin List that uses a simple, thread-safe, round-robin load balancing algorithm where it provides the next item in the list each time the Next method is called and starts back at the top of the list when it reaches the end of the list.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Helpers
{
/// <summary>
/// Creates a round robin list that uses a simple, thread-safe, round-robin
/// load balancing algorithm where it provides the next item in the list each
/// time <see cref="Next"/> is called and starts back at the top of the list
/// when it reaches the end of the list.
/// </summary>
/// <typeparam name="T">List type.</typeparam>
public class RoundRobinList<T>
{
private readonly IList<T> _list;
private readonly int _size;
private int _position;
private readonly object _lock;
/// <summary>
/// Creates a round robin list that uses a simple, thread-safe, round-robin
/// load balancing algorithm where it provides the next item in the list each
/// time <see cref="Next"/> is called and starts back at the top of the list
/// when it reaches the end of the list.
/// </summary>
/// <param name="list">List of items.</param>
public RoundRobinList(IEnumerable<T> list)
{
var enumerable = list as T[] ?? list.ToArray();
if (list == null || !enumerable.Any())
throw new NullReferenceException(nameof(list));
_list = new List<T>(enumerable);
_size = _list.Count;
_lock = new object();
}
/// <summary>
/// Gets the next item from the list using a round-robin algorithm.
/// When the end of the list is reached it starts back at the top of
/// the list and repeats indefinitely.
/// </summary>
/// <returns>Next item from the list.</returns>
public T Next()
{
if (_size == 1)
{
return _list[0];
}
lock (_lock)
{
if (_position == _size)
{
_position = 0;
}
return _list[_position++];
}
}
}
}
using NUnit.Framework;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
namespace Helpers
{
[TestFixture]
public class RoundRobinListTests
{
[Test]
[Category("Positive Test")]
[Category("Unit Test")]
public void Should_Start_Back_At_The_Top_Of_The_List()
{
var list = new [] {1, 2, 3};
var roundRobinList = new RoundRobinList<int>(list);
Assert.That(roundRobinList.Next(), Is.EqualTo(list[0]));
Assert.That(roundRobinList.Next(), Is.EqualTo(list[1]));
Assert.That(roundRobinList.Next(), Is.EqualTo(list[2]));
Assert.That(roundRobinList.Next(), Is.EqualTo(list[0]));
Assert.That(roundRobinList.Next(), Is.EqualTo(list[1]));
Assert.That(roundRobinList.Next(), Is.EqualTo(list[2]));
}
[Test]
[Category("Positive Test")]
[Category("Unit Test")]
public void Should_Return_Same_Number_Of_Items_When_Called_From_Concurrent_Threads()
{
var list = new [] { 1, 2, 3 };
var roundRobinList = new RoundRobinList<int>(list);
const int iterations = 5000;
var items = new ConcurrentBag<int>();
Parallel.For(0, list.Length * iterations, _ =>
{
items.Add(roundRobinList.Next());
});
Assert.That(items.Count(x => x == list[0]), Is.EqualTo(iterations));
Assert.That(items.Count(x => x == list[1]), Is.EqualTo(iterations));
Assert.That(items.Count(x => x == list[2]), Is.EqualTo(iterations));
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment