CSharp · Console
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Linq;
using System.Runtime.CompilerServices;
public static class Program
{
public static void Main()
{
ThreadSafeData<(int, int, int, int, int, int, int, int, int)> data = new();
data.Init();
using CancellationTokenSource cts = new(1000);
int writerSpinCount = 0;
int readerSpinCount = 0;
int readersCount = 4;
Task<int> writer = new Task<int>(() =>
{
int iterations = 0;
Random random = new();
while (!cts.IsCancellationRequested)
{
iterations++;
int value = random.Next();
data.SetData((value, value, value, value, value, value, value, value, value));
Thread.SpinWait(writerSpinCount);
}
return iterations;
});
Task<(int, int)>[] readers = Enumerable.Range(1, readersCount).Select(n => new Task<(int, int)>(() =>
{
int iterations = 0;
int tornValues = 0;
while (!cts.IsCancellationRequested)
{
iterations++;
var d = data.GetData();
if (d.Item1 != d.Item2 || d.Item1 != d.Item3 || d.Item1 != d.Item4 || d.Item1 != d.Item5 || d.Item1 != d.Item6 || d.Item1 != d.Item7 || d.Item1 != d.Item8 || d.Item1 != d.Item9)
{
tornValues++;
if (tornValues == 1) Console.WriteLine($"Reader #{n}, First torn value: {d}");
}
Thread.SpinWait(readerSpinCount);
}
return (iterations, tornValues);
})).ToArray();
Console.WriteLine($"Type: {data.GetType().Name}, Tuple.Length: {((ITuple)data.GetData()).Length:#,0}");
Console.WriteLine($"Writer (1) SpinCount: {writerSpinCount:#,0}");
Console.WriteLine($"Reader ({readers.Length}) SpinCount: {readerSpinCount:#,0}");
Console.WriteLine();
ThreadPool.SetMinThreads(readers.Length + 1, 4);
Task allTasks = Task.WhenAll(readers.Cast<Task>().Prepend(writer).ToArray());
long mem0 = GC.GetTotalAllocatedBytes(true);
Array.ForEach(readers, r => r.Start(TaskScheduler.Default));
writer.RunSynchronously(TaskScheduler.Default);
allTasks.Wait();
long mem1 = GC.GetTotalAllocatedBytes(true);
Console.WriteLine($"Writer iterations: {writer.Result:#,0}");
Console.WriteLine($"Reader iterations: {String.Join(", ", readers.Select(r => $"{r.Result.Item1:#,0}"))} (Sum: {readers.Select(r => r.Result.Item1).Sum():#,0})");
int sumTornValues = readers.Select(r => r.Result.Item2).Sum();
if (sumTornValues > 0) Console.WriteLine($"Torn values: {String.Join(", ", readers.Select(r => $"{r.Result.Item2:#,0}"))} (Sum: {sumTornValues:#,0})");
Console.WriteLine($"Allocated: {mem1 - mem0:#,0} bytes");
}
public struct ThreadSafeData<T>
{
private T[] dataArray;
private int setterIndex;
private int lastSetIndex;
public void Init()
{
dataArray = new T[2];
}
public void SetData(T data)
{
dataArray[setterIndex] = data;
lastSetIndex = setterIndex;
// Convert 0 to 1, and 1 to 0
setterIndex = lastSetIndex * - 1 + 1;
}
public T GetData()
{
return dataArray[lastSetIndex];
}
}
}