Планировщик () — параллельный шаблон проектирования, обеспечивающий механизм реализации политики планирования, но при этом не зависящий ни от одной конкретной политики. Управляет порядком, в соответствии с которым потокам предстоит выполнить последовательный код, используя для этого объект, который явным образом задаёт последовательность ожидающих потоков.
Мотивы Несколько потоков могут потребовать доступ к ресурсу одновременно, и только один поток в какой-то момент времени может осуществить доступ к ресурсу. Согласуясь с требованиями программы, потоки должны осуществлять доступ к ресурсу в определенном порядке.
Пример реализации
Пример C#
using System;
namespace Digital_Patterns.Concurrency.Sheduler { class Printer
private static Int32 mID = 0;
private Scheduler _scheduler = new Scheduler();
public void Print(JournalEntry journalEntry)
Int32 id = ++mID; try
Console.WriteLine(String.Format(@"{0}: enter scheduler", id)); // вызов не выполнится до тех пор, пока объект Scheduler не решит, // что подошла очередь распечатать этот объект JournalEntry _scheduler.Enter(journalEntry); Console.WriteLine(String.Format(@"{0}: start printing", id)); try
//TODO Something journalEntry.Do(id);
finally
// вызов метода Done говорит Scheduler о том, что объект JournalEntry // распечатан, и может подойти очередь вывода на печать другого объекта // JournalEntry _scheduler.Done(); Console.WriteLine(String.Format(@"{0}: done scheduler", id));
catch (Exception) {}
using System; using System.Collections.Generic; using System.Threading;
namespace Digital_Patterns.Concurrency.Sheduler { /// <summary> /// Экземпляры классов в этой роли управляют обработкой объектов Request <see cref="JournalEntry"/>, /// выполняемой объектом Processor <see cref="Printer"/>. Чтобы быть независимыми от типов /// запросов, класс <see cref="Scheduler"/> не должен ничего знать об управляемом им классе Request. /// Вместо этого он осуществляет доступ к объектам Request через реализуемый ими интерфейс <see cref="ISchedulerOrdering"/> /// </summary> class Scheduler
/// <summary> /// Объект синхронизации потоков /// </summary> private AutoResetEvent _event = new AutoResetEvent(false);
/// <summary> /// Устанавливается в null, если управляемый объектом Scheduler ресурс не занят. /// </summary> private Thread _runningThread;
/// <summary> /// Потоки и их запросы ожидающие выполнения /// </summary> private Dictionary<Thread, ISchedulerOrdering> _waiting = new Dictionary<Thread, ISchedulerOrdering>();
/// <summary> /// Метод <see cref="Enter"/> вызывается перед тем, как поток начнет использовать управляемый ресурс. /// Метод не выполняется до тех пор пока управляемый ресурс не освободится и объект <see cref="Sheduler"/> /// не примет решение, что подошла очередь выполнения этого запроса /// </summary> /// <param name="s"></param> public void Enter(ISchedulerOrdering s)
var thisThread = Thread.CurrentThread;
lock(this)
// Определяем не занят ли планировщик if(_runningThread == null)
// Немедленно начинаем выполнение поступившего запроса _runningThread = thisThread; return;
_waiting.Add(thisThread, s);
lock(thisThread)
//Блокируем поток до тех пор, пока планировщик не решит сделать его текущим while(thisThread != _runningThread)
_event.WaitOne(); _event.Set(); // даем возможность другим потокам проверить своё состояние Thread.Sleep(1);
_event.Reset();
lock (this)
_waiting.Remove(thisThread);
/// <summary> /// Вызов метода <see cref="Done"/> указывает на то, что текущий поток завершил работу /// и управляемый ресурс освободился /// </summary> public void Done()
lock (this)
if (_runningThread != Thread.CurrentThread) throw new ThreadStateException(@"Wrong Thread");
Int32 waitCount = _waiting.Count; if (waitCount <= 0)
_runningThread = null;
else if (waitCount == 1)
_runningThread = _waiting.First().Key; _waiting.Remove(_runningThread); _event.Set();
else
var next = _waiting.First(); foreach (var wait in _waiting)
if(wait.Value.ScheduleBefore(next.Value))
next = wait;
_runningThread = next.Key; _event.Set();
/// <summary> /// Вспомогательный класс /// </summary> static partial class ConvertTo
/// <summary> /// Получить первый элемент коллекции /// </summary> /// <param name="collection"></param> /// <returns></returns> public static KeyValuePair<Thread, ISchedulerOrdering> First(this Dictionary<Thread, ISchedulerOrdering> collection)
foreach (var item in collection)
return item;
throw new ArgumentException();
using System;
namespace Digital_Patterns.Concurrency.Sheduler { /// <summary> /// Если несколько операций ожидают доступа к ресурсу, класс<see cref="Scheduler"/> использует /// данный интерфейс для определения порядка выполнения операций. /// </summary> interface ISchedulerOrdering
Boolean ScheduleBefore(ISchedulerOrdering s);
using System; using System.Threading;
namespace Digital_Patterns.Concurrency.Sheduler { /// <summary> /// Примерный код класса <see cref="JournalEntry"/>, который должен быть /// распечатан классом <see cref="Printer"/> /// </summary> class JournalEntry : ISchedulerOrdering
private static DateTime mTime = DateTime.Now;
private DateTime _time;
/// <summary> /// Возвращает время создания этого объекта /// </summary> public DateTime Time { get { return _time; } }
private String _msg;
public JournalEntry(String msg)
mTime = mTime.AddSeconds(1); _time = mTime; _msg = msg;
public void Do(Int32 id)
Console.WriteLine(String.Format(@"{0}: Start doing : {1} : {2}", id, _time, _msg)); Thread.Sleep(1000); Console.WriteLine(String.Format(@"{0}: Finish do : {1} : {2}", id, _time, _msg));
/// <summary> /// Возвращает true, если данный запрос должен /// обрабатываться перед этим запросом. /// </summary> /// <param name="s"></param> /// <returns></returns> public Boolean ScheduleBefore(ISchedulerOrdering s)
if(s is JournalEntry)
var otherJournalEntry = (JournalEntry) s; return (this.Time < otherJournalEntry.Time);
return false;
using System; using System.Threading;
namespace Digital_Patterns.Concurrency.Sheduler { public class Example01
private Printer _printer;
public void Run()
Console.WriteLine(@"Press any key for start, and press again for finish"); Console.ReadKey();
_printer = new Printer(); new Thread(Thread1).Start(); new Thread(Thread2).Start(); new Thread(Thread3).Start();
Console.ReadKey();
private void Thread1()
var msg1 = new JournalEntry(@"Buy toll 5.45 USD"); var msg2 = new JournalEntry(@"Buy candy 1.05 USD"); var msg3 = new JournalEntry(@"Buy chocolate 3.25 USD"); _printer.Print(msg1); _printer.Print(msg2); _printer.Print(msg3);
private void Thread2()
var msg4 = new JournalEntry(@"Buy postcard 2.05 USD"); var msg5 = new JournalEntry(@"Buy gerland 37.78 USD"); _printer.Print(msg4); _printer.Print(msg5);
private void Thread3()
var msg6 = new JournalEntry(@"Buy ball 30.06 USD"); var msg7 = new JournalEntry(@"Buy pipe 1.83 USD"); _printer.Print(msg6); _printer.Print(msg7);
using System; using Digital_Patterns.Concurrency.Sheduler;
namespace Digital_Patterns { class Program
static void Main(string[] args)
new Example01().Run();
Console.WriteLine(@"Press any key for end"); Console.ReadKey();
Ссылки (см. синопсис )
Категория:Шаблоны проектирования