RosettaCodeData/Task/Atomic-updates/C-sharp/atomic-updates.cs

126 lines
3.9 KiB
C#

using System; //Rand class
using System.Threading; //Thread, Mutex classes
public class ThreadSafeBuckets
{
//This class is thread safe, and ensures that all operations on it are atomic.
//Calling threads do not need to ensure safety.
Random rand = new Random();
int[] Buckets;
object[] locks; //Mutexes for each bucket so they can lock individually
public int BucketCount { get; private set; }
public ThreadSafeBuckets(int bucketcount)
{
//Create buckets+mutexes and fill them with a random amount
BucketCount = bucketcount;
Buckets = new int[bucketcount];
locks = new object[bucketcount];
int startingtotal = 0;
for (int i = 0; i < BucketCount; i++)
{
locks[i] = new object();
Buckets[i] = rand.Next(30);
startingtotal += Buckets[i];
}
//Print the starting total
Console.WriteLine("Starting total: " + startingtotal);
}
public int GetBucketValue(int i)
{
return Buckets[i];
}
public void Transfer(int i, int j, int amount)
{
//Transfer amount from bucket i to bucket j
if (i > BucketCount || j > BucketCount || i < 0 || j < 0 ||
i == j || amount < 0)
return;
//To prevent deadlock, always lock the lower bucket first
lock (locks[Math.Min(i, j)])
lock (locks[Math.Max(i, j)])
{
//Make sure don't transfer out more than is in the bucket
amount = Math.Min(amount, Buckets[i]);
//Do the transfer
Buckets[i] -= amount;
Buckets[j] += amount;
}
}
public void PrintBuckets()
{
int counter = 0;
//Lock all the buckets in sequential order and print their contents
for (int i = 0; i < BucketCount; i++)
{
Monitor.Enter(locks[i]);
Console.Write(Buckets[i] + " ");
counter += Buckets[i];
}
//Print the bucket total, then unlock all the mutexes
Console.Write("= " + counter);
Console.WriteLine();
foreach (var l in locks)
Monitor.Exit(l);
}
}
class Program
{
static ThreadSafeBuckets TSBs;
public static void Main(){
//Create the thread-safe bucket list
TSBs = new ThreadSafeBuckets(10);
TSBs.PrintBuckets();
//Create and start the Equalizing Thread
new Thread(new ThreadStart(EqualizerThread)).Start();
Thread.Sleep(1);
//Create and start the Randamizing Thread
new Thread(new ThreadStart(RandomizerThread)).Start();
//Use this thread to do the printing
PrinterThread();
}
//EqualizerThread runs on it's own thread and randomly averages two buckets
static void EqualizerThread()
{
Random rand = new Random();
while (true)
{
//Pick two buckets
int b1 = rand.Next(TSBs.BucketCount);
int b2 = rand.Next(TSBs.BucketCount);
//Get the difference
int diff = TSBs.GetBucketValue(b1) - TSBs.GetBucketValue(b2);
//Transfer to equalize
if (diff < 0)
TSBs.Transfer(b2, b1, -diff / 2);
else
TSBs.Transfer(b1, b2, diff/2);
}
}
//RandomizerThread redistributes the values between two buckets
static void RandomizerThread()
{
Random rand = new Random();
while (true)
{
int b1 = rand.Next(TSBs.BucketCount);
int b2 = rand.Next(TSBs.BucketCount);
int diff = rand.Next(TSBs.GetBucketValue(b1));
TSBs.Transfer(b1, b2, diff);
}
}
//PrinterThread prints the current bucket contents
static void PrinterThread()
{
while (true)
{
Thread.Sleep(50); //Only print every few milliseconds to let the other threads work
TSBs.PrintBuckets();
}
}
}