- •Сборки (assembly) в среде .Net. Проблема версионности сборок и ее решение.
- •Номер версии в .Net
- •Сведения о версии
- •Номер версии сборки
- •Информационная версия сборки
- •Общая система типов данных в среде .Net. Размерные и ссылочные типы данных. Типы, переменные и значения
- •Пользовательские типы
- •Система общих типов cts
- •Ссылочные типы
- •Типы литеральных значений
- •Неявные типы, анонимные типы и типы, допускающие значение null
- •Упаковка и распаковка размерных типов данных в среде .Net.
- •Производительность
- •Упаковка–преобразование
- •Распаковка-преобразование
- •Ссылочные типы данных. Объектная модель в среде .Net и языке c#.
- •Модели ручной и автоматической утилизации динамической памяти, их сравнительная характеристика. Модель с ручным освобождением памяти
- •Модель с автоматической «сборкой мусора»
- •Модель автоматической утилизации динамической памяти, основанная на сборке мусора. Проблема недетерминизма.
- •Модель автоматической утилизации динамической памяти, основанная на аппаратной поддержке (тегированной памяти).
- •Сборка мусора в среде .Net. Построение графа достижимых объектов.
- •Сборка мусора в среде .Net. Механизм поколений объектов.
- •Модель детерминированного освобождения ресурсов в среде .Net. Интерфейс iDisposable и его совместное использование с завершителем (методом Finalize).
- •«Мягкие ссылки» и кэширование данных в среде .Net.
- •Краткие и длинные слабые ссылки
- •Краткая ссылка
- •Длинная ссылка
- •Правила использования слабых ссылок
- •Динамические массивы в среде .Net и языке c#.
- •Приведение типов в массивах
- •Все массивы неявно реализуют /Enumerable, /Collection и iList
- •Передача и возврат массивов
- •Создание массивов с ненулевой нижней границей
- •Производительность доступа к массиву
- •Небезопасный доступ к массивам и массивы фиксированного размера
- •Делегаты в среде .Net и механизм их работы. Знакомство с делегатами
- •Использование делегатов для обратного вызова статических методов
- •Использование делегатов для обратного вызова экземплярных методов
- •Правда о делегатах
- •Использование делегатов для обратного вызова множественных методов (цепочки делегатов)
- •Поддержка цепочек делегатов в с#
- •Расширенное управление цепочкой делегатов
- •Упрощение синтаксиса работы с делегатами в с#
- •Упрощенный синтаксис № 1: не нужно создавать объект-делегат
- •Упрощенный синтаксис № 2: не нужно определять метод обратного вызова
- •Упрощенный синтаксис № 3: не нужно определять параметры метода обратного вызова
- •Упрощенный синтаксис № 4: не нужно вручную создавать обертку локальных переменных класса для передачи их в метод обратного вызова
- •Делегаты и отражение
- •События в среде .Net; реализация событий посредством делегатов. События
- •Этап 1: определение типа, который будет хранить всю дополнительную информацию, передаваемую получателям уведомления о событии
- •Этап 2: определение члена-события
- •Этап 3: определение метода, ответственного за уведомление зарегистрированных объектов о событии
- •Этап 4: определение метода, транслирующего входную информацию в желаемое событие
- •Как реализуются события
- •Создание типа, отслеживающего событие
- •События и безопасность потоков
- •Явное управление регистрацией событий
- •Конструирование типа с множеством событий
- •Исключительные ситуации и реакция на них в среде .Net. Достоинства
- •Механика обработки исключений
- •Блок try
- •Блок catch
- •Блок finally
- •Генерация исключений
- •Определение собственных классов исключений
- •Исключения в платформе .Net Framework
- •Исключения и традиционные методы обработки ошибок
- •Управление исключениями средой выполнения
- •Фильтрация исключений среды выполнения
- •21 Средства многопоточного программирования в среде .Net. Автономные потоки. Пул потоков.
- •Создание и использование потоков
- •Запуск и остановка потоков
- •Методы управления потоками
- •Безопасные точки
- •Свойства потока
- •Потоки Windows в clr
- •К вопросу об эффективном использовании потоков
- •Пул потоков в clr
- •Ограничение числа потоков в пуле
- •22. Асинхронные операции в среде .Net. Асинхронный вызов делегатов.
- •23. Синхронизация программных потоков в среде .Net. Блокировки.
- •Двойная блокировка
- •Класс ReaderWriterLock
- •Использование объектов ядра Windows в управляемом коде
- •Вызов метода при освобождении одного объекта ядра
- •24. Синхронизация программных потоков в среде .Net. Атомарные (Interlocked-операции). Семейство lnterlocked-методов
- •25. Прерывание программных потоков в среде .Net. Особенности исключительной ситуации класса ThreadAbortException.
- •26. Мониторы в среде .Net. Ожидание выполнения условий с помощью методов Wait и Pulse. Класс Monitor и блоки синхронизации
- •«Отличная» идея
- •Реализация «отличной» идеи
- •Использование класса Monitor для управления блоком синхронизации
- •Способ синхронизации, предлагаемый Microsoft
- •Упрощение кода c# при помощи оператора lock
- •Способ синхронизации статических членов, предлагаемый Microsoft
- •Почему же «отличная» идея оказалась такой неудачной
- •Целостность памяти, временный доступ к памяти и volatile-поля
- •Временная запись и чтение
- •Поддержка volatile-полей в с#
- •27. Асинхронный вызов делегатов.
- •Общие типы (Generics)
- •Инфраструктура обобщений
- •Открытые и закрытые типы
- •Обобщенные типы и наследование
- •Проблемы с идентификацией и тождеством обобщенных типов
- •«Распухание» кода
- •Обобщенные интерфейсы
- •Обобщенные делегаты
- •Обобщенные методы
- •Логический вывод обобщенных методов и типов
- •Обобщения и другие члены
- •Верификация и ограничения
- •Основные ограничения
- •Дополнительные ограничения
- •Ограничения конструктора
- •Другие вопросы верификации
- •Приведение переменной обобщенного типа
- •Присвоение переменной обобщенного типа значения по умолчанию
- •Сравнение переменной обобщенного типа с null
- •Сравнение двух переменных обобщенного типа
- •Использование переменных обобщенного типа в качестве операндов
- •Преимущества использования общих типов
- •29. Итераторы в среде .Net. Создание и использование итераторов.
- •Общие сведения о итераторах
Поддержка volatile-полей в с#
Сложно сделать так, чтобы все программисты вызывали методы VolatileRead и VolatileWrite корректно. Разработчикам сложно помнить обо всем этом и прогнозировать, что могут делать с общими данными другие потоки, работающие в фоновом режиме. Компилятор C# предлагает ключевое слово volatile, которое можно применять к статическим полям или экземплярам полей следующих типов: Byte, SByte,Intl6, UIntl6,Jnt32, UInt32, Char, Single или Boolean. Это ключевое слово можно также использовать для ссылки на типы и любые поля перечислений, если в основе перечислимого типа лежит тип Byte, SByte, Intl6, UIntl6, Int32, UInt32, Single или Boolean. Компилятор JIT гарантирует, что все обращения к volatile-полям выполняются по механизму временного считывания и записи, так что не обязательно явно вызывать какие-либо статические методы VolatileXxx. Кроме того, ключевое слово volatile сообщает компиляторам С# и JIT, что поле не нужно кешировать в регистре процессора, в этом случае во всех операциях с этим полем будет использоваться значение, считанное из основной памяти.
Ключевое слово volatile позволяет на основе кода класса VolatileMethod создать новый класс VolatileField:
internal sealed class VolatileField {
private volatile Byte uninitialized = 0; private Int32 m_value = 0;
// Этот метод выполняется одним потоком, public void Thread1() {
m_value =5;
uninitialized = 1;
// Этот метод выполняется другим потоком, public void Thread2() {
if (uninitialized == 1) {
// Если выполняется эта строка, отобразится значение 5.
Console.WriteLine(m_value); }
Некоторым разработчикам на С# (и мне в том числе) не нравится ключевое слово volatile — они считают, что в языке программирования этого слова быть не должно. Они полагают, что в большинстве алгоритмов требуется совсем немного операций временного чтения или записи в поле, а большинство обращений можно выполнить обычным путем, что, к тому же, положительно скажется на производительности. Редко требуется, чтобы все операции доступа к полю были временными. Например, сложно сказать, как выполнять операции временного считывания в алгоритмах, подобных этому:
m_amount = m_amount + m_amount; // m_amount - определенное в классе поле. m_amount *= m_amount
Кроме того, С# не поддерживает передачу volatile-полей в метод по ссылке. Например, если поле m_amount определено как volatile Int32, при попытке вызвать метод TryParse типа Int32 компилятор выдаст предупреждение:
Boolean success = Int32.TryParse("123", out m_amount);
// При обработке этой строки кода компилятор С# покажет следующее предупреждение:
// CS0420: a reference to a volatile field will not be treated as volatile
// (CS0420: ссылка на volatile-поле не будет считаться временной).
Создавая JIT-компилятор для архитектуры IA64, разработчики CLR поняли, что у многих программистов (включая их самих) есть код, который не будет корректно работать при вызове не из временных (неупорядоченных) операций записи и чтения. Поэтому они решили, что JIT-компилятор IA64 должен всегда создавать инструкции чтения, включающие семантику запроса, а операции записи должны всегда включать семантику освобождения. Это позволило существующим приложениям, работающим в архитектуре х8б, работать без ошибок и в архитектуре IA64. К сожалению, это сильно ударило бы по производительности, поэтому пришлось пойти на компромисс. Во всех операциях записи в JIT-компилятореIА64 используется семантика освобождения, а чтение выполняется в обычном порядке. Чтобы выполнить чтение с семантикой запроса, нужно вызвать метод VolatileRead или применить ключевое слово volatile. Это намного более понятная модель управления памятью, которую разработчики вполне в состоянии освоить.
Microsoft обещает, что все имеющиеся и любые будущие JIT-компиляторы будут четко придерживаться этой модели памяти, в которой используются обычные операции чтения, но запись всегда выполняется с семантикой освобождения. Если быть совсем точным, правила доступа к памяти немного сложнее. Впрочем, большинство программистов об этом догадывается.
Примечание:Как говорилось в главе 1, ассоциация ЕСМА определила стандартную версию CLR, названную Common Language Infrastructure (CLI). В документации ЕСМА описывается модель памяти, которой должны соответствовать все CLI-совместимые среды времени выполнения. В этой версии модели памяти все операции чтения и записи являются неупорядоченными, если только программист явно не вызывает метод VolatileRead или VolatileWrite или не использует ключевое слово volatile. Это также объясняет, почему FCL все еще содержит методы VolatileWrite, хотя они бесполезны, если приложение работает в CLR, созданной компанией Microsoft. В Microsoft посчитали модель ЕСМА слишком сложной и непонятной, поэтому реализовали собственную версию CLI (то есть CLR), использующую более сильную модель памяти, в которой все операции записи выполняются с семантикой освобождения. Такое изменение позволяет считать CLR компании Microsoft совместимой со стандартом, так как любые приложения, написанные для модели памяти ЕСМА, могут работать в CLR от Microsoft.
Однако существует возможная проблема, заключающаяся в том, что кто-то может написать приложение и протестировать его при помощи CLR компании Microsoft. Это приложение может отлично работать, но в другой реализации CLI оно может не работать. Было бы лучше для всех, если бы стандарт ЕСМА и все реализации этого стандарта придерживались единой модели памяти. Microsoft надеется, что в следующих версиях стандарта ЕСМА будет принята модель памяти, которая сейчас применяется в CLR.
Наконец, нужно сказать, что всегда, когда поток вызывает Interlocked-метод (см. следующий раздел), процессор принудительно обеспечивает согласованность кеша. Так что при работе с переменными при помощи Interlocked-методов не нужно беспокоиться о соответствии этой модели памяти. Кроме того, все блокировки синхронизации потоков (Monitor, ReaderWriterLock,Mutex, Semaphore, AutoResetEvent, ManualResetEvent и другие) вызывают Interlocked-мегоды, так что программисту также не приходится заботиться о моделях памяти.
Внимание! В общем случае, я настоятельно не рекомендую вызывать методы VolatileRead и VolatileWrite, а также ключевое слово volatile. Вместо этого лучше использовать Interlocked-методы или конструкции синхронизации потоков более высокого уровня. Эти методы работают всегда, независимо от модели памяти и платформы процессора.