
- •Конспект лекций по курсу «Сети эвм» Часть 2 Сурков д.А.
- •Сравнительная характеристика технологий .Net и Java
- •Общее для Java и .Net.
- •Управление памятью в .Net
- •Процедурные типы данных (делегаты)
- •Динамические массивы
- •Многозадачность
- •1: Добавление в очередь ThreadPool нового делегата, который будет запускаться в отдельном потоке (из пула свободного потока). Делегат отработал – поток обратно в пул.
- •Исключительные ситуации
- •Шаблоны в с# (появились недавно в .Net 2.0)
- •В с# в шаблонах введено понятие итератора.
- •Массивы
- •Метод Get в SystemArray
- •Средства удалённого вызова .Net Remoting
- •Пользовательские атрибуты
- •Inherited – будем ли создавать атрибут для наследника.
- •Защищённые информационные системы. Технология “Эльбрус”
- •Защищенная файловая система
1: Добавление в очередь ThreadPool нового делегата, который будет запускаться в отдельном потоке (из пула свободного потока). Делегат отработал – поток обратно в пул.
Операция создания и завершения потоков довольно трудоемкая операция, поэтому создавать поток каждый раз при появлении делегата, а потом удалять – трата времени. Для пула потоки созданы, и если нет делегатов, они просто «спят». Количество потоков в пуле стандартно равно 30 (или 32) и может быть изменено свойством ThreadPool.a. Если все потоки заняты, и вызывается функция 1, то делегат ставится в очередь и ждет, пока освободится поток. Делегаты, которые ждут, не потребляют процессорного времени и дают возможность другим делегатам закончить свою работу.
Пул потоков является глобальным и создается автоматически при первом обращении к 1 (QueueUserWorkItem).
Но основная проблема при создании многопоточных программ состоит в том, что все потоки разделяют все глобальные данные в домене приложения. При доступе к данным надо выполнять синхронизацию (как при чтении, так и при записи). Тут проблема: стандартные средства синхронизации ОС – критические секции – переменная, которая хранит состояние, управляет конструктором ОС (войти в критическую секцию – выйти из критической секции). Но критическая секция является крайне неудобной при работе с динамическими структурами данных, т.к. синхронизировать доступ надо не между блоками кода, а между доступом к данным конкретных объектов.
Пример: доступ к БД. Данные, изменяемые одним потоком, должны быть видны для других потоков. Если какой-то поток обращается для чтения (записи) данных, другие потоки не должны этого делать. Если используем критическую секцию, то один поток может изменять все данные, другие потоки вынуждены ждать.
Т1 меняет и в Заказах, и в Потребителях, а если Т2 хочет изменить другую запись в Заказах, он не может, т.к. должен ждать завершения Т1.
Можно: критические секции для пометки блоков кода нужно переименовать в мьютексы (семафоры); использовать критические секции надо с данными. С каждым элементом записи ассоциировать свою критическую секцию, и перед обращением к объекту установить «занято» в критической секции, а перед выходом – «свободно». В этом случае единицей блокировки выступает не код программы, а одна запись таблицы (т.е. данные). Если Т1 и Т2 обращаются к одной записи, то Т2 подождет, пока Т1 завершит работу с этой записью. А в других случаях потоки работают параллельно. Но недостатки: необходимо связывать с каждым объектом системы критическую секцию, т.е. в каждый объект – наследник класса Object, надо вкладывать мьютекс, который потенциально может использоваться при многопоточном режиме работы.
Разработчики .Net сделали следующее (т.к. критические секции занимают много места) – критические секции объединены в пул. Для ссылки на используемую критическую секцию в каждом объекте в базовый класс Object вложено неявное поле:
SyncBlockIndex стандартно -1. Это номер критической секции в пуле критических секции.
Если происходит синхронизация при доступе к объекту с использованием Monitor.Enter (Obj) – управляет пулом критических секции, то это вход в критическую секцию.
Если записано -1, то из пула критических секции вызывается свободная критическая секция (С3), связывается с объектом и в поле записывается новое значение (2), что указывает на эту критическую секцию. Далее выполняется Enter для этой критической секции.
После работы с объектом в критической секции выполняется выход из критической секции, т.е. разблокирование объекта, чтобы с ним могли работать другие потоки:
Monitor.Leave (Obj);
Критическая секция разблокируется, но номер критической секции останется записанным в объекте до тех пор, пока объект не будет удален. При удалении объекта сборщик мусора освобождает критическую секцию в пуле (номер 2 свидетельствует о том, что для объекта есть критическая секция , а то занят объект или нет – в пуле).
При использовании критической секции надо следить за тем, чтобы метод Leave вызывался всегда, даже тогда, когда при работе с объектом возникает исключение. Используются операторы try…finally:
Monitor.Enter (Obj);
try
{
Obj.DoSomeThing( );
…
}
finally
{ этот блок срабатывает всегда
Monitor.Leave(Obj);
}
Эта вся схема на С# с помощью оператора lock может быть записана так:
lock (Obj)
{
Obj.DoSomething( );
}
Monitor.Enter, _try…_finally, Monitor.Leave генерируются в этом случае автоматически.