126 lines
3.9 KiB
C#
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();
|
|
}
|
|
}
|
|
}
|