10) Технология отражения / Работа с потоками выполнения

Технология отражения
При создании сборки в неё помещаются метаданные, которые являются описанием всех типов в сборке и их элементов. Программист может работать с метаданными, используя специальный механизм, называемый отражением (reflection). Главные элементы, которые необходимы для использования возможностей отражения, – это класс System.Type1 и типы из пространств имён System.Reflection и System.Reflection.Emit. Класс Type служит для хранения информации о типе.
Существует несколько способов получить объект этого класса: 
1. Вызвать у объекта метод GetType().присутствует у любого объекта:
Foo foo = new Foo(); /* Foo – это некий класс */ Type t = foo.GetType();
2. Использовать статический метод Type.GetType(), которому передаётся имя типа в виде строки. Type t1 = Type.GetType("System.Int32"); Type t2 = Type.GetType("SomeNamespace.Foo, MyAssembly");
3. Использовать операцию C# typeof: Type t = typeof (Foo); Операцию typeof можно применять к массивам и универсальным шаблонам. Причём в последнем случае допускается использовать как сконструированный тип, так и исходный тип-шаблон.
Type t1 = typeof (int[]); Type t2 = typeof (char[,]); Type t3 = typeof (List<int>); //
сконструированный тип ;Type t4 = typeof (List<>); // универсальный тип
Свойства класса Type позволяют узнать имя типа, имя базового типа, является ли тип универсальным, в какой сборке он размещается и другую информацию. Кроме этого, Type имеет специальные методы, возвращающие данные о полях типа,
свойствах, событиях, методах и их параметрах. Информация об элементах типа хранится в объектах классов FieldInfo, PropertyInfo, MethodInfo и т. п. Эти классы находятся в пространстве имён System.Reflection.Составом получаемой информации можно управлять, передавая в Get-методы дополнительные флаги System.Reflection.BindingFlags (var bf = BindingFlags.Public)(метода стр 100). Пространство имён System.Reflection содержит типы для получения ин- формации и манипулирования сборкой и модулем сборки. При помощи класса Assembly можно получить информацию о сборке, при помощи класса Module – о модуле. Продемонстрируем пример работы с классами Assembly и Module:
Assembly assembly = Assembly.GetExecutingAssembly();
Console.WriteLine(assembly.FullName);
foreach (Module module in assembly.GetModules())
{
Console.WriteLine(module.FullyQualifiedName);
foreach (Type type in module.GetTypes())
{
Console.WriteLine(type.FullName);
}

Работа с потоками выполнения
Платформа .NET даёт полноценную поддержку для создания многопоточных приложений. Исполняющая среда имеет особый модуль, ответственный за организацию многопоточности, но в основном работа модуля опирается на функции многопоточности операционной системы. Основные классы, предназначенные для поддержки многопоточности,сосредоточены в пространстве имён System.Threading. На платформе .NET каждый поток выполнения (thread) представлен объектом класса Thread. Для организации собственного потока необходимо создать объект этого класса. Класс Thread имеет четыре перегруженные версии конструктора:
public Thread(ThreadStart start);
public Thread(ThreadStart start, int maxStackSize);
public Thread(ParameterizedThreadStart start);
public Thread(ParameterizedThreadStart start, int maxStackSize);
В качестве 1 аргумента конструктору передаётся делегат, инкапсулирующий метод, выполняемый в потоке. Доступно два типа делегатов: 2 позволяет при запуске метода передать ему данные в виде объекта: public delegate
void ThreadStart(); public delegate void ParameterizedThreadStart(object obj);
Создание потока не подразумевает его автоматического запуска. Для запуска потока требуется вызвать метод Start() (перегруженная версия метода получает объект, передаваемый как аргумент методу потока).
var th = new Thread(DoSomeWork); th.Start();
Рассмотрим основные свойства класса Thread:
1. CurrentThread возвращает объект, представляющий текущий поток.
2. Свойство Name служит для назначения потоку имени.
3. Целочисленное свойство для чтения ManagedThreadId возвращает уни-
кальный числовой идентификатор управляемого потока.
4. Свойство для чтения ThreadState, позволяет получить состояние потока.
5. Булево свойство IsAlive позволяет определить, выполняется ли поток.
6. Свойство Priority управляет приоритетом выполнения потока относи-
тельно текущего процесса.
7. Булево свойство IsBackground позволяет сделать поток фоновым. 
Среда исполнения .NET разделяет все потоки на фоновые и основные. Процесс не мо- жет завершиться, пока не завершены все его основные потоки. Метод Suspend() вызывает приостановку потока, метод Resume() возобновляет ра- боту потока. Статический метод Sleep() приостанавливает выполнение текущего потока на указанное количество миллисекунд или значение TimeSpan. Статический метод Yield() передаёт управление следующему ожидающему потоку системы. Метод Join() позволяет дождаться завершения работы того потока, у которого вызывается. Для завершения работы выбранного потока используется метод Abort(). Данный метод генерирует специальное исключение ThreadAbortException. Создание отдельного потока – это довольно «затратная» операция с точки зрения расхода времени и памяти. Для уменьшения издержек, связанных с созданием потоков, платформа .NET поддерживает специальный механизм, называе- мый пул потоков. Пул состоит из двух основных элементов: очереди методов и рабочих потоков. Характеристикой пула является его ёмкость – максимальное число рабочих потоков. При работе с пулом метод сначала помещается в очередь. Если у пула есть свободные рабочие потоки, метод извлекается из очереди и направляется свободному потоку для выполнения. Если свободных потоков нет, но ёмкость пула не достигнута, для обслуживания метода формируется но- вый рабочий поток. Однако этот поток создаётся с задержкой в полсекунды. Если за это время освободится какой-либо из рабочих потоков, то он будет назначен на выполнение метода, а новый рабочий поток создан не будет. Важным нюан- сом является то, что несколько первых рабочих потоков в пуле создаётся без полусекундной задержки.
Для работы с пулом используется статический класс ThreadPool. Метод SetMaxThreads() позволяет изменить ёмкость пула. Метод SetMinThreads() устанавливает количество рабочих потоков, создаваемых без задержки. Для помещения метода в очередь пула служит метод QueueUserWorkItem().