Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
!Шпора по ООПиП (3).docx
Скачиваний:
35
Добавлен:
22.09.2019
Размер:
2.31 Mб
Скачать

97. Шаблоны параллельного программирования Шаблоны параллельного программирования (Concurrency)

  • Active Object

  • Balking

  • Double checked locking

Double checked locking (блокировка с двойной проверкой) — шаблон проектирования применяющийся в параллельном программировании. Он предназначен для уменьшения накладных расходов, связанных с получением блокировки. Сначала проверяется условие блокировки без какой-либо синхронизации; поток делает попытку получить блокировку только если результат проверки говорит о том, что ни один другой поток не владеет блокировкой.

На некоторых языках и/или на некоторых машинах невозможно безопасно реализовать данный шаблон. Поэтому иногда его называют анти-паттерном.

Обычно он используется для уменьшения накладных расходов при реализации ленивой инициализации в многопоточных программах, например в составе шаблона проектирования Одиночка. При ленивой инициализации переменной, инициализация откладывается до тех пор, пока значение переменной действительно понадобится при вычислениях.

public sealed class Singleton

{

private Singleton()

{

// инициализировать новый экземпляр объекта

}

 

private static volatile Singleton singletonInstance;

 

private static readonly Object syncRoot = new Object();

 

public static Singleton GetInstance()

{

// создан ли объект

if(singletonInstance == null)

{

// нет, не создан

// только один поток может создать его

lock(syncRoot))

{

// проверяем, не создал ли объект другой поток

if(singletonInstance == null)

{

// нет не создал — создаём

singletonInstance = new Singleton();

}

}

}

return singletonInstance;

}

}

Microsoft подтверждает [5], что при использовании ключевого слова volatile, использование паттерна Double checked locking является безопасным.

  • Guarded suspension

  • Half-Sync/Half-Async

  • Leaders/followers

  • Monitor Object

  • Reactor

  • Read write lock

  • Scheduler

Планировщик (англ. Scheduler) — Шаблон проектирования обеспечивающий механизм реализации политики планирования, но при этом не зависит ни от одной конкретной политики. Управляет порядком, в соответствии с которым потокам предстоит выполнить последовательный код, используя для этого объект, который явным образом задаёт последовательность ожидающих потоков.

Мотивы

  • Несколько потоков могут потребовать доступа к ресурсу одновременно, и только один поток в какой-то момент времени может осуществить доступ к ресурсу.

  • Согласуясь с требованиями программы, потоки должны осуществлять доступ к ресурсу в определенном порядке.

Пример реализации Пример 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"/> не должен ничего знать об управляемом им классе Requect.

/// Вместо этого он осущуствляет доступ к объектам 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();

}

}

}

  • Thread pool

  • Thread-Specific Storage

  • Single Thread Execution

Однопоточное выполнение (англ. Single Threaded Execution) — известный также под названием англ. Critical Section. Шаблон проектирования препятствующий конкурентному вызову метода, тем самым запрещая параллельное выполнение этого метода.

Мотивы

  • Класс содержит методы, которые обновляют или задают значения в переменных экземпляра класса или переменных класса.

  • Метод манипулирует внешними ресурсами, которые поддерживают только одну операцию в какой-то момент времени.

  • Методы класса могут вызываться параллельно различными потоками.

  • Не существует временного ограничения, которое требовало бы от метода немедленного выполнения, как только его вызывают.