Skip to content

Instantly share code, notes, and snippets.

@bgourlie
Created January 28, 2012 21:46
Show Gist options
  • Select an option

  • Save bgourlie/1695855 to your computer and use it in GitHub Desktop.

Select an option

Save bgourlie/1695855 to your computer and use it in GitHub Desktop.
A solution I wrote to the Dining Philosophers problem (http://en.wikipedia.org/wiki/Dining_philosophers_problem) using C#.
/*
* Solves the dining philosphers problem using manual syncronization. Also solves for any arbitrary number of philosophers,
* which can be changed by altering the NUM_PHILOSPHERS constant.
*
* Besides solving the original problem, this solution also implements fairness.
*
* http://en.wikipedia.org/wiki/Dining_philosophers_problem
*/
using System.Threading;
using System;
using System.Threading.Tasks;
namespace DiningPhilosophers
{
public class DiningPhilosophers
{
public static Random Rand = new Random();
public static readonly int NUM_PHILOSOPHERS = 20;
public class Fork
{
public bool IsDirty = true;
public readonly int Id;
public Philosopher HeldBy;
public Philosopher[] Contenders = new Philosopher[2];
public Philosopher GetContender(Philosopher whosAsking)
{
return (Contenders[0] == whosAsking) ? Contenders[1] : Contenders[0];
}
public Fork(int id)
{
Id = id;
}
}
public class Philosopher
{
private int _foodRemaining = 5;
private const int FORK_LEFT = 0;
private const int FORK_RIGHT = 1;
public readonly int Id;
private readonly Fork[] _forks = new Fork[2];
private readonly Fork[] _iRequestedFork = new Fork[2];
private readonly Fork[] _theyRequestedFork = new Fork[2];
public event Action<Fork> RequestFork;
public event Action<Fork, Philosopher> SubmitFork;
public Philosopher(int id, Fork right, Fork left)
{
Id = id;
_forks[FORK_RIGHT] = right;
_forks[FORK_LEFT] = left;
}
public void WireEvents()
{
var leftForkContender = _forks[FORK_LEFT].GetContender(this);
var rightForkContender = _forks[FORK_RIGHT].GetContender(this);
leftForkContender.RequestFork += OnForkRequestedFromContender;
rightForkContender.RequestFork += OnForkRequestedFromContender;
leftForkContender.SubmitFork += OnForkSubmittedByContender;
rightForkContender.SubmitFork += OnForkSubmittedByContender;
}
public void OnForkRequestedFromContender(Fork fork)
{
if (fork.HeldBy != this) return;
if (fork.IsDirty) //its dirty, so give it up
{
Console.WriteLine("p{0} received request for f{1} from p{2} and offers it.", Id, fork.Id,
fork.GetContender(this).Id);
lock (fork)
{
fork.IsDirty = false; //clean fork
fork.HeldBy = null; //release fork
}
SubmitFork(fork, fork.GetContender(this));
}else //its clean so we keep it, but note their request
{
Console.WriteLine("p{0} received request for f{1} from p{2} but its in use.", Id, fork.Id,
fork.GetContender(this).Id);
_theyRequestedFork[(fork == _forks[FORK_LEFT]) ? FORK_LEFT : FORK_RIGHT] = fork;
}
}
public void OnForkSubmittedByContender(Fork fork, Philosopher intendedFor)
{
if (intendedFor != this) return;
_iRequestedFork[(fork == _forks[FORK_LEFT]) ? FORK_LEFT : FORK_RIGHT] = null;
lock (fork) fork.HeldBy = this;
Console.WriteLine("p{0} takes f{1} from p{2} (forks held: {3})", Id, fork.Id, fork.GetContender(this).Id, GetForksHeldString());
}
private string GetForksHeldString()
{
if (_forks[FORK_LEFT].HeldBy == this && _forks[FORK_RIGHT].HeldBy == this) return "both";
if (_forks[FORK_LEFT].HeldBy == this) return "left";
if (_forks[FORK_RIGHT].HeldBy == this) return "right";
return "none";
}
public void Run()
{
while (_foodRemaining > 0)
{
Console.WriteLine("p{0} is thinking (forks held: {1})", Id, GetForksHeldString());
while (!Array.TrueForAll(_forks, fork => fork.HeldBy == this))
{
Thread.Sleep(100);
foreach(var fork in _forks)
{
if (fork.HeldBy != this && Array.IndexOf(_iRequestedFork, fork) == -1)
{
_iRequestedFork[(fork == _forks[FORK_LEFT]) ? FORK_LEFT : FORK_RIGHT] = fork;
Console.WriteLine("p{0} requesting f{1} from p{2} (forks held: {3})", Id, fork.Id, fork.GetContender(this).Id, GetForksHeldString());
RequestFork(fork);
}
}
}
--_foodRemaining;
Console.WriteLine("p{0} is eating, {1} food left.", Id, _foodRemaining);
Thread.Sleep(Rand.Next(1000) + 200); //wait at least 200ms, up to 1200ms
_forks[FORK_LEFT].IsDirty = true;
_forks[FORK_RIGHT].IsDirty = true;
Thread.Sleep(Rand.Next(500) + 100); //wait at least 500ms, up to 600ms
int combo = 1;
while (_foodRemaining > 0 && Array.TrueForAll(_theyRequestedFork, fork => fork == null))
{
lock (this)
{
Console.WriteLine("p{0} is eating (x{2}), {1} food left.", Id, --_foodRemaining, ++combo);
Thread.Sleep(100);
}
}
foreach(var fork in _forks)
{
if(Array.IndexOf(_theyRequestedFork, fork) != -1) //submit the fork to any previous requestor
{
Console.WriteLine("p{0} offers f{1} to p{2} (previous request)", Id, fork.Id, fork.GetContender(this).Id);
_theyRequestedFork[(fork == _forks[FORK_LEFT]) ? FORK_LEFT : FORK_RIGHT] = null;
SubmitFork(fork, fork.GetContender(this));
}
}
}
Console.WriteLine("p{0} is done eating.", Id);
}
}
private static void Main()
{
var forks = new Fork[NUM_PHILOSOPHERS];
for (int i = 0; i < NUM_PHILOSOPHERS; i++) { forks[i] = new Fork(i); }
var phils = new Philosopher[NUM_PHILOSOPHERS];
var threads = new Task[NUM_PHILOSOPHERS];
int curFork = 0;
for (int i = 0; i < NUM_PHILOSOPHERS; i++)
{
var rightFork = forks[curFork];
var leftFork = forks[(curFork != NUM_PHILOSOPHERS - 1) ? ++curFork : 0];
phils[i] = new Philosopher(i, rightFork, leftFork);
rightFork.Contenders[0] = phils[i];
leftFork.Contenders[1] = phils[i];
threads[i] = new Task(phils[i].Run, TaskCreationOptions.LongRunning);
}
Array.ForEach(forks, //place forks
fork =>
fork.HeldBy =
(fork.Contenders[0].Id > fork.Contenders[1].Id) ? fork.Contenders[1] : fork.Contenders[0]);
Array.ForEach(phils, phil => phil.WireEvents()); //neighboring philosophers communicate via events
Array.ForEach(threads, thread => thread.Start()); //start each thread
Console.ReadLine();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment