
Scheduler/Планировщик
Категория: Шаблоны параллельного программирования
Описание
Планировщик - паттерн параллельного программирования, который используется для обеспечения явного контроля за ситуацией, когда разные потоки могут выполнять куски однопоточного кода (операции записи в файл и т.д.).
Суть паттерна заключается в создании объекта, который явным образом ставит в очередь ожидающие ресурс потоки.
Уместность применения
Шаблон следует применять, если:
Несколько потоков могут потребовать доступа к ресурсу одновременно, и только один из них может в конкретный момент осуществить доступ к ресурсу
Требуется обеспечение доступа потоков к ресурсу в строго определенном порядке
Преимущества, достигаемые при применении шаблона
При использовании шаблона «Планировщик» объект планировщика независим от политики, по которой упорядочиваются потоки, что упрощает повторное использование кода.
Недостатки шаблона
Отсутствие способа обмена информацией между ведомыми компонентами зачастую существенно усложняет код.
Детали реализации
Классы
class Scheduler
{
private AutoResetEvent evnt = new AutoResetEvent(false);
private Thread runningThread;
private Dictionary<Thread, IOrdering> waiting = new Dictionary<Thread, IOrdering>();
public void Enter(IOrdering s)
{
var thisThread = Thread.CurrentThread;
lock (this)
{
if (runningThread == null)
{
runningThread = thisThread;
return;
}
waiting.Add(thisThread, s);
}
lock (thisThread)
{
while (thisThread != runningThread)
{
evnt.WaitOne();
evnt.Set();
Thread.Sleep(1);
}
evnt.Reset();
}
lock (this)
{
waiting.Remove(thisThread);
}
}
public void Done()
{
lock (this)
{
if (runningThread != Thread.CurrentThread)
throw new ThreadStateException(@"Wrong Thread");
int waitCount = waiting.Count;
if (waitCount <= 0)
{
runningThread = null;
}
else if (waitCount == 1)
{
runningThread = waiting.First().Key;
waiting.Remove(runningThread);
evnt.Set();
}
else
{
var next = waiting.First();
foreach (var wait in waiting)
{
if (wait.Value.ScheduleBefore(next.Value))
{
next = wait;
}
}
runningThread = next.Key;
evnt.Set();
}
}
}
}
class BankAccount
{
private static int transactionID;
private Scheduler scheduler = new Scheduler();
private int balance = 0;
public void Handle(Transaction transaction)
{
int id = ++transactionID;
try
{
Console.WriteLine(String.Format(@"Transaction #{0}: enter scheduler", id));
scheduler.Enter(transaction);
Console.WriteLine(String.Format(@"Transaction #{0}: start handling", id));
try
{
transaction.Do(id);
balance += transaction.Chng;
}
finally
{
scheduler.Done();
Console.WriteLine(String.Format(@"Transaction #{0}: done! Account balance is {1}", id, balance));
}
}
catch (Exception) { }
}
}
interface IOrdering
{
Boolean ScheduleBefore(IOrdering s);
}
class Transaction : IOrdering
{
private static DateTime mTime = DateTime.Now;
private DateTime time;
public DateTime Time { get { return time; } }
private int chng;
public int Chng
{
get { return chng; }
}
public Transaction(int chng)
{
mTime = mTime.AddSeconds(1);
time = mTime;
this.chng = chng;
}
public void Do(int id)
{
Console.WriteLine(String.Format(@"Transaction #{0}: Start : {1} : Balance change: {2} roubles", id, time, chng));
Thread.Sleep(1000);
Console.WriteLine(String.Format(@"Transaction #{0}: Finish : {1} : Balance change: {2} roubles", id, time, chng));
}
public Boolean ScheduleBefore(IOrdering s)
{
if (s is Transaction)
{
var otherTransaction = (Transaction)s;
return (this.Time < otherTransaction.Time);
}
return false;
}
}
Программа:
static BankAccount bankAccount;
static void Thread1()
{
var msg1 = new Transaction(10);
var msg2 = new Transaction(15);
var msg3 = new Transaction(112);
bankAccount.Handle(msg1);
bankAccount.Handle(msg2);
bankAccount.Handle(msg3);
}
static void Thread2()
{
var msg4 = new Transaction(50);
var msg5 = new Transaction(100);
bankAccount.Handle(msg4);
bankAccount.Handle(msg5);
}
static void Thread3()
{
var msg6 = new Transaction(-80);
var msg7 = new Transaction(20);
bankAccount.Handle(msg6);
bankAccount.Handle(msg7);
}
static void Main(string[] args)
{
bankAccount = new BankAccount();
new Thread(Thread1).Start();
new Thread(Thread2).Start();
//new Thread(Thread3).Start();
Console.ReadKey();
}
UML-диаграмма
Диаграмма классов:
Диаграмма последовательности
Литература
http://en.wikipedia.org/wiki/Scheduler_pattern
http://ru.wikipedia.org/wiki/Scheduler