Определение делегатов в качестве статических членов
В С# можно определять метод, который используется при создании делегата как статический член класса.
Составные делегаты
Объединение делегатов — создание одного делегата из нескольких.
Эта возможность языка позволяет динамически определять, какие методы нужно включать в метод обратного вызова, и объединять их в составной делегат. Исполняющая среда распознает, что эти методы нужно вызвать последовательно.
Определение событий с помощью делегатов
Работа с событиями в С# соответствует модели "издатель — подписчик", где класс публикует событие, которое он может инициировать, и любые классы могут подписаться на это событие. При инициации события исполняющая среда следит за тем, чтобы уведомить всех подписчиков о возникновении события.
Метод, вызываемый при возникновении события, определяется делегатом. Однако нужно помнить об ограничениях. Во-первых, нужно чтобы такой делегат принимал два аргумента. Во-вторых, эти аргументы всегда должны представлять два объекта: тот, что инициировал событие (издатель), и информационный объект события, который должен быть производным от класса EventArgs .NET Framework.
Интерфейсы
Интерфейсы определяются с помощью ключевого слова interface, как показано в следующем примере.
interface IEquatable<T>
{ bool Equals(T obj);}
Интерфейсы описывают группу связанных функциональных возможностей, которые могут принадлежать к любому классу или структуре. Интерфейсы в могут содержать методы, свойства, события, индексаторы или любое сочетание этих перечисленных типов членов. Интерфейсы не могут содержать поля. Члены интерфейсов автоматически являются открытыми.
Классы и структуры могут быть унаследованы от интерфейсом таким же образом, как классы могут быть унаследованы от базового класса или структуры, но есть два исключения:
· Класс или структура может наследовать несколько интерфейсов.
· Когда класс или структура наследует интерфейс, наследуются только имена и подписи методов, поскольку сам интерфейс не содержит реализаций.
Для реализации члена интерфейса соответствующий член класса должен быть открытым и не статическим, он должен обладать таким же именем и подписью, как член интерфейса.
Интерфейс имеет следующие свойства.
· Интерфейс подобен абстрактному базовому классу: любой неабстрактный тип, наследующий интерфейс, должен реализовать все его члены.
· Невозможно создать экземпляр интерфейса напрямую.
· Интерфейсы могут содержать методы, свойства, индексаторы и события в качестве членов.
· Интерфейсы не содержат реализации методов.
· Как классы, так и структуры способны наследовать от нескольких интерфейсов.
· Интерфейс может быть унаследован от нескольких интерфейсов.
Программирование многопоточности. Потоки и многозадачность. Переключение контекста. Безопасность и синхронизация потоков.
Потоки и многозадачность
Поток является единицей обработки данных, а многозадачность — это одновременное исполнение нескольких потоков. Существует два вида многозадачности — совместная (cooperative) и вытесняющая (preemptive). При этом процессор отвечает за выдачу каждому потоку определенного количества времени, в течение которого поток может выполняться, — кванта времени (timeslice). Далее процессор переключается между разными потоками, выдавая каждому потоку его квант времени.
Кстати, даже в случае вытесняющей многозадачности, если вы работаете на однопроцессорной машине, то все равно в любой момент времени реально будет исполняться только один поток. Поскольку интервалы между переключениями процессора от процесса к процессу измеряются миллисекундами, возникает иллюзия многозадачности. Чтобы несколько потоков на самом деле работали одновременно, вам потребуется работать на многопроцессорной машине, создав соответствующую программу.
Переключение контекста
Неотъемлемый атрибут потоков — переключение контекста (context switching)
Процессор с помощью аппаратного таймера определяет момент окончания кванта, выделенного для данного потока. Когда аппаратный таймер генерирует прерывание, процессор сохраняет в стеке содержимое всех регистров для данного потока. Затем процессор перемещает содержимое этих же регистров в структуру данных CONTEXT. При необходимости переключения обратно на поток, выполнявшийся прежде, процессор выполняет обратную процедуру и восстанавливает содержимое регистров из структуры CONTEXT, ассоциированной с потоком. Весь этот процесс называется переключением контекста.
Многопоточное приложение на С#
using System.Threading;
class SimpleThreadApp {
Public static void WorkerThreadMethodQ
{Console.WriteLine("Worker thread started");}
public static void Main() {
ThreadStart worker = new ThreadStart(WorkerThreadMethod);
Console.WriteLine("Main - Creating worker thread");
Thread t = new Thread(worker); t.StartQ;
Console.WriteLine("Main - Have requested the start of worker thread"); } }
Скомпилировав и запустив это приложение, вы увидите, что сообщение метода Main выводится перед сообщением рабочего потока. Это доказывает, что рабочий поток действительно работает асинхронно. Проанализируем происходящие здесь события.
ThreadStart — это делегат. Именно он должен быть задействован при создании нового потока. Он используется, чтобы задать метод, который должен вызываться как метод потока. Здесь я создаю экземпляр объекта Thread, при этом конструктор принимает в качестве аргумента только делегат ThreadStart:
Thread t = new Thread(worker);
После этого я вызываю метод Start объекта Thread, в результате чего вызывается метод WorkerThreadMethod.
Работа с потоками
Создание потоков и управление ими осуществляется с помощью класса System. Threading. Thread. С него мы и начнем.
В .NET потоки работают в сущности под названием AppDomain: это логический процесс внутри физического процесса.
Класс Thread
Практически все, что вы делаете с потоками, вы делаете, используя класс Thread. В этом разделе класс Thread рассматривается в контексте решения основных задач организации потоков.