- •Конспект лекций по курсу «Сети эвм» Часть 2 Сурков д.А.
- •Сравнительная характеристика технологий .Net и Java
- •Общее для Java и .Net.
- •Управление памятью в .Net
- •Процедурные типы данных (делегаты)
- •Динамические массивы
- •Многозадачность
- •1: Добавление в очередь ThreadPool нового делегата, который будет запускаться в отдельном потоке (из пула свободного потока). Делегат отработал – поток обратно в пул.
- •Исключительные ситуации
- •Шаблоны в с# (появились недавно в .Net 2.0)
- •В с# в шаблонах введено понятие итератора.
- •Массивы
- •Метод Get в SystemArray
- •Средства удалённого вызова .Net Remoting
- •Пользовательские атрибуты
- •Inherited – будем ли создавать атрибут для наследника.
- •Защищённые информационные системы. Технология “Эльбрус”
- •Защищенная файловая система
Многозадачность
Параллельно выполняются подпрограммы и их синхронизация. Механизмы, заложенные для многозадачности в .Net, превосходят механизмы ОС Windows, UNIX.
Многозадачность в .Net, является многоуровневой. Существует понятие потока. Понятие процесса заменено – домен приложения.
Поток – наименьшая единица диспетчеризации в .Net. Потоки работают параллельно в соответствии с приоритетом. Потоки с более высоким приоритетом всегда вытесняют менее приоритетные.
В .Net существуют классы, которые являются классами-оболочками для объектов ОС-мы (среди них Thread).
Но наряду с ними существуют средства более эффективной многозадачности и более эффективные средства синхронизации.
Основная проблема многозадачных программ: все потоки в рамках одного домена приложения разделяют все глобальные данные, включая все динамические объекты в heap. Поэтому для доступа ко всем объектам ссылочных типов необходимо выполнять синхронизацию, гарантируя, что два потока не обратятся к свойствам одного объекта. Эта синхронизация – не только при записи, но и при чтении.
Требование синхронизации при чтении обусловлено тем, что программа может работать на многопроцессорном компьютере, для которого может возникнуть следующая проблема:
Есть два ЦП, у каждого из них есть свой КЭШ. Может оказаться следующее: в памяти есть объект, который читается двумя процессорами. Этот объект попадает в КЭШ ОП. Он же попадает в КЭШ процессора. При этом, если ЦП1 меняет объект, эти данные аппаратно переписываются в КЭШ-память ОП. Но у ЦП2 данные остаются в КЭШе. Поэтому без специальных механизмов синхронизации поток на ЦП2 читает старые данные. Т.о. необходима операция синхронизации КЭШа.
Аппаратные комплексы: ошибка, даже если просто чтение двух процессоров одного КЭШа. Поэтому синхронизация необходима всегда.
Для делегатов:
void delegate ControlChanged (Control c); // это делегат
class My
{
void ButtonChanged (Button b);
… (b as Button)
}
есть класс:
class Control
{
ControlChanged; // в методе ButtonChanged этот делегат установить нельзя
// (т.к. другая сигнатура), надо преобразовать с использованием as
}
class Button: Control
{
…
}
А если по-другому:
void delegate ButtonChanged (Button b);
class My
{
void ControlChanged (Control c);
…
}
class Control
{
};
class Button: Control
{
ButtonChanged Changed; // этому методу можно присвоить ControlChanged
// без преобразований
}
Продолжаем потоки.
Многопоточность создается с помощью thread и с помощью объединения «спуллинга».
using System;
using System.Threading;
public class ThreadExample
{
public static void ThreadProc ( ) // 1
{
Console.WriteLine (“Tread Started”); // поток стартовал
Thread.Sleep (1000); // поток засыпает
Console.WriteLine (“Thread Finishing”); // поток завершается
} // теперь поток завершен
public static void Main ( )
{
Console.WriteLine (“ Starting Thread”);
Thread t=new Thread (new ThreadStart (threadProc)); // 2-создаем объект, в
// конструктор которого передается делегат (ThreadStart), который
// выполняется в отдельном потоке
t.Start ( ); // 3
Tread.Sleep(0); // 4
for (int i=0; i<1000; i++)
{
Console.WriteLine (“Main Thread doing some work”);
Thread.Sleep (100);
};
Console.WriteLine (“Waiting secondary thread”);
t.Join ( ); // 5-один поток (главный) ждет другой поток (ждет завершения
// вторичного)
Console.WriteLine (“Tread Finished”);
};
Процедура, выполняющаяся в потоке, должна быть объявлена как
1: public stasic void ThreadProc ( ) – без параметров (static - необязательно)
2: new ThreadStart (ThreadProc) – это создание не объекта, а делегата, куда в качестве параметра передается объект потока
3: t.Start ( ) - запуск потока. Но после выполнения этого метода поток не всегда физически возникает (в однопроцессорных машинах), а создается, когда завершится главный поток
4: если напишем Thread.Sleep(0), то главный поток прерывается, и управление передается дочернему (вторичному) потоку.
5: главный поток ожидает вторичный поток (поток t), а не наоборот.
Это использование потоков, которые (объект Thread) отображаются ОС-мой. Объект Thread используется, когда надо гарантированно выделить другой поток.
Второй пример:
using System;
using System.Threading;
public class ThreadExample
{
public static void ThreadProc (Object stateInfo )
{
Console.WriteLine (“Tread Started”);
Thread.Sleep (1000);
Console.WriteLine (“Thread Finishing”);
}
public static void Main ( )
{
Console.WriteLine (“ Starting Thread”);
ThreadPool.QueueUserWorkItem(new WaitCallback (ThreadProc)); // 1
for (int i=0; i<1000; i++)
{
Console.WriteLine (“Main Thread doing some work”);
Thread.Sleep (100);
}
}
}
Этот способ используется чаще. Для первого метода недостатки: если пришло 1000 запросов, то будет создано 1000 потоков. Поэтому все запросы клиентов будут работать медленно. Обычно используется 8 потоков на 1 процесс (один процессор) – это наиболее эффективно, и надо создавать поток, не когда пришел запрос, а когда используется некоторый пул потоков. Если работы слишком мало, то некоторые потоки отдыхают (ничего не делают), а если много задач – то потоки ставятся в очередь.