postsharp - threading model library
TRANSCRIPT
1
© L
uxo
ft T
rain
ing
20
12
PostSharp Threading Pattern Library
Андрей Гордиенков
softblog.violet-tape.ru
2
Обо мне
Фанат программирования и рассказов о программировании
Ведет свой блог 5+ года: статьи и видео
АОП евангелист
Очень ленив, поэтому ищет пути как писать меньше, а делать больше
3
Немного из истории языков
Ранние языки строилась вокруг модели памяти
Оперирование переменными, а не адресами памяти
FORTRAN I (1955)
Дальнейшее развитие пошло вокруг модели исполнения
Внедрение методов и подпрограмм
FORTRAN II (1958)
Внедрение областей видимости
ALGOL 60
Объединение концепций
COBOL
Скорее всего первый ООП язык был SIMULA
C++, C# и Java прямые наследники SIMULA
4
Качества хорошей модели программирования
Адекватные абстракции
Проверяемость
Локальность и разделение ответственности
Детерминизм
Производительность
5
Threading Pattern Library
Предоставляет высокоуровневый механизм управления моделями
многопоточности для кода.
Проверка времени компиляции и исполнения.
Присутствует в PostSharp Express.
Реализованные модели:
Immutable
Freezable
Synchronized
Reader/Writer Synchronized
Actor
Thread Affine
Thread Unsafe
6
Необходимые условия работы моделей
Для корректной работы может потребоваться использование специальных
коллекций
AdvisableCollection<T>
AdvisableDictionary<TKey,TValue>
Поддержка концепции агрегации и композиции фреймворком.
[Child]
[Parent]
[Reference]
7
Пример
[Aggregatable]public class Invoice {
[Child]public readonly AdvisableCollection<InvoiceLine> Lines = new AdvisableCollection<InvoiceLine>();
[Reference]public Customer Customer;
}
[Aggregatable]public class InvoiceLine {
[Reference]public Product Product;
[Parent]public Invoice ParentInvoice { get; private set; }
}
8
Ключевые идеи PostSharp TPL
Основной фокус направлен на познавательные аспекты программирования
Понятия Immutable, Actor, Freezable, Locking могут относиться к многопоточности
Модели многопоточности относятся к классам, а не к приложению и модулям
Разные модели многопоточности могут сосуществовать в одном приложении
Шаблоны проектирования могут быть реализованы расширением языка, а не
кучей кода
Можно точно определить, что есть валидный код на уровне классов. Исключая
инвариантность.
Использование агрегации/композиции как основы для формализации шаблонов
9
Immutable
Суть: Объект не может быть изменен после того, как выполнилась вся цепочка
конструкторов.
Подход в целом неадекватен для ООП.
Инициализация объекта часто продолжается после создания объекта.
Никак не регулируется изменяемость объектов принадлежащих классу.
Фиксирование объекта происходит после выполнения последнего
конструктора в цепочке.
Изменятся могут только поля/свойства помеченные [Reference]
10
Пример
[Immutable]public class Invoice {
public long Id { get; set; }public Invoice(long id) {
Id = id;Items = new AdvisableCollection<Item>();Items.Add(new Item("widget"));
}
[Child]public AdvisableCollection<Item> Items { get; set; }
}
11
Freezable
Суть: Перед обработкой объекта в других потоках, его надо «заморозить».
Операция не обратимая.
Рабочая модель для идеи «неизменяемых» объектов. В реализации
руководствовались следующим:
Доступ к объекту запрещен для других потоков, пока объект не был заморожен. Даже на
чтение.
После «заморозки» объект не может быть изменен ни при каких обстоятельствах.
Метод Freeze() замораживает также все агрегируемые свойства
«Дети» должны реализовывать модель Immutable или Freezable
12
Freezable
Применение аспекта внедряет в класс интерфейс IFreezable
public interface IFreezable : IThreadAware {
void Freeze();
}
13
Пример
[Freezable]public class Invoice {
[Child]public readonly AdvisableCollection<InvoiceLine> Lines = new AdvisableCollection<InvoiceLine>();
[Reference]public Customer Customer;
}
[Freezable]public class InvoiceLine {
[Reference]public Product Product;
[Parent]public Invoice ParentInvoice { get; private set; }
}
14
Synchronized
Суть: Критические секции класса оборачиваются конструкцией lock(), которая
позволяет обрабатывать (читать/писать) себя только одному потоку
одновременно.
«Классика жанра», самая популярная модель доступа к данным в
многопоточной среде.
Главный подозреваемый при организации deadlock и задержке потоков.
Падение производительности происходит при работе с долгими операциями ввода\вывода
Реализация основана на классе Monitor
15
Пример
[Synchronized]public class OrderService {
public void Process(int sequence) {Console.WriteLine("sequence {0}", sequence);Console.WriteLine("sleeping for 10s");
Thread.Sleep(new TimeSpan(0, 0, 10));}
}
16
Reader-Writer Synchronized
Суть: Раздельные уровни блокировки частей кода для чтения и для записи. В
один момент времени чтение позволено многим потокам, запись – только
одному.
Public и Internal методы класса должны быть помечены атрибутами:
[Reader]
[Writer]
[UpgradableReader]
Get/Set автоматически помечаются как [Reader]/[Writer]
Реализация основана на ReaderWriterLockSlim классе
17
Пример
[ReaderWriterSynchronized]public class Order {
public decimal Amount { get; set; }public decimal Discount { get; set; }[Child]public AdvisableCollection<Item> lines = new AdvisableCollection<Item>();
public decimal AmountAfterDiscount {get { return Amount - Discount; }
}
[Writer]public void Set(decimal amount, decimal discount) {
if (amount < discount) {throw new InvalidOperationException();
}
Amount = amount;Discount = discount;
}}
18
Actor
Суть: Запросы класса асинхронно перенаправляются в одну очередь исполнения
в которой выполняются по порядку в один поток.
Любой public/internal метод упаковывается в сообщение, которое кладется в
очередь
Класс реализует интерфейс IActor
Реализация основана на классе ConcurrentQueue
Шаблон требует, чтобы все методы относились к следующим категориям:
Ничего не возвращали в результате работы
Были асинхронными. Помечены словом async
public interface IActor : IThreadAware
{
IActorDispatcher Dispatcher { get; }
}
19
Пример
[Actor]public class AverageCalculator {
private float sum;private int count;
public void AddSample(float n) {count++;sum += n;
}
[Reentrant]public async Task<float> GetAverage() {
return sum / count;}
}
20
Thread Affine
Суть: Методы класса выполняется только в том потоке, в котором был создан
класс.
Реализация просто запоминает поток создания объекта.
Никаких особенностей =)
21
Пример
[ThreadAffine]public class OrderService {
public void Process(int sequence) {Console.WriteLine("sequence {0}", sequence);Console.WriteLine("sleeping for 10s");
Thread.Sleep(new TimeSpan(0, 0, 10));}
}
22
Thread Unsafe
Суть: При одновременном доступе к классу с нескольких потоков, выбрасывается
исключение.
Не рекомендуется к использованию.
Существует только для диагностики проблем многопоточности.
Вызывает исключение, если смена значения происходит в другом потоке.
Реализовано с помощью операции CompareExchange
23
Пример
[ThreadUnsafe]internal class AverageCalculator {
private float sum;private int count;
public void AddSample(float n) {count++;sum += n;
}
public float GetAverage() {return sum / count;
}}
24
Особенности использования
Синхронные методы возвращающие результат должны быть помечены
атрибутом [ExplicitlySynchronized]
Для таких методов/свойств просто не будет производится проверка
Если точка входа в класс – приватный метод, то его надо обозначить
атрибутом [EntryPoint]
Например, через делегат к приватному методу.
25
Ограничения при использовании
Ограниченная проверка на этапе сборки
PostSharp проверяет структурную композицию, а не поведение элементов
Блокирующее ожидание в async методах
Все async методы должны быть помечены атрибутом [Reentrant]
Нет асинхронного ожидания
Нет защиты хоста
Не проводились тесты на наличие исключений OutOfMemoryException или
ThreadAbortException в процессе работы.
26
Написание своей валидации
Фактически любой базовый аспект позволяет переопределить самостоятельно
метод CompileTimeValidate() для валидации на этапе компиляции
[Serializable]public class MethodAttribute : OnMethodBoundaryAspect {
public override bool CompileTimeValidate(MethodBase method) {
}}
[Serializable]public class TypeAspect : TypeLevelAspect {
public override bool CompileTimeValidate(Type type) {
}}
27
Вопросы
28
Ссылки
Github https://github.com/VioletTape/PS_TPL_Examples
PostSharp http://www.postsharp.net