Слайд 1Язык С#
Сборки, потоки и домены приложений
Лекция #5
Слайд 2Common Object Model
Повторное использование кода
Создание COM-серверов и регистрация их в
реестре (HREY_CLASSES_ROOT)
Проблема версий COM
Запись в реестре и сам двоичный файл
COM не связаны друг с другом
Слайд 3Обзор сборок .NET
Приложение .NET – объединение любого количества сборок
Сборка –
это двоичный файл (DLL или EXE), который содержит номер версии,
метаданные, а также типы и дополнительные ресурсы
Манифест – набор метаданных о самой сборке
Слайд 4Обзор сборок .NET
Логическое представление сборки (классы, интерфейсы, ресурсы, делегаты)
Физическое представление
(набор модулей – файлов)
Сборки обеспечивают повторное использование кода, написанного на
любом языке .NET
Сборки – контейнеры для типов (не будет конфликта имен)
Разные версии сборок могут выполнятся одновременно
Слайд 5Создание однофайловой сборки
Слайд 6Добавление ссылки на внешнюю сборку
Слайд 7Библиотека кода - CarLibary
namespace CarLibrary
{ using System;
public enum EngineState
// Для двух возможных состояний двигателя
{ engineAlive,
engineDead
}
public abstract class Car
// Абстрактный класс — базовый в нашей будущей иерархии
{
// Защищенные данные о состоянии
protected string petName;
protected short currSpeed;
protected short maxSpeed;
protected EngineState egnState;
public Car() {egnState = EngineState.engineAlive;}
public Car(string name, short max, short curr)
{ egnState = EngineState.EngineAlive;
petName = name; maxSpeed = max; currSpeed = curr; }
public string PetName { get { return petName; } set { petName = value; } }
public short CurrSpeed { get { return currSpeed; } set { currSpeed = value; } }
public short MaxSpeed { get {return maxSpeed; } }
public EngineState EngineState { get { return egnState; } }
public abstract void TurboBoost();
}}
Слайд 8Реализация конкретных классов SportsCar и MiniVan
namespace CarLibrary
{ using System;
using
System.Windows.Forms; // Чтобы можно было использовать MessageBox
public class SportsCar
: Car // Определение класса SportsCar
{
// Конструкторы
public SportsCar(){}
public SportsCar(string name, short max, short curr) : base (name, max, curr) {}
// Специфическая реализация метода TurboBoost()
public override void TurboBoost()
{
MessageBox.Show("Ramming speed!", "Faster is better...");
}
}
// Определение класса MiniVan
public class Minivan : Car
{
// Конструкторы
public MiniVan(){}
public MiniVan(string name, short max, short curr) : base (name, max, curr){}
// Реализация метода TurboBoost()
{
// Мини-вэны разгоняются неважно
egnState = EngineState.engineDead;
MessageBox.Show("Time to call AAA", "Your car is dead");
}
}
}
Слайд 9Клиентское приложение
При добавлении в проекте ссылки на сборку
в каталог
DEBUG полностью копируется DLL-файл
Слайд 10Клиентское приложение
// Первый опыт использования собственной библиотеки кода
namespace CSharpCarClient
{
using System;
//
Используем типы из CarLibrary
using CarLibrary;
public class CarClient
{
public static int Main(string[]
args)
{
// Создаем автомобиль спортивной модели
SportsCar viper = new SportsCar("Viper", 240, 40);
viper.TurboBoost();
// Создаем мини-вэн
MiniVan mv = new MiniVan();
mv.TurboBoost();
return 0;
}
}
}
Слайд 13Частные сборки
private или shared
Находятся в каталоге приложения или в подкаталогах
Можно
переносить каталог с приложением
Можно просто все удалить
Слайд 14Алгоритм поиска
В каталоге приложения dll
В каталоге приложения exe
Конфигурационный файл
Слайд 15Конфигурационный файл
xmlns="urn:schemas-microsoft-com:asm.v1">
CSSharpClient.exe → CSSharpClient.exe.config
Слайд 16Сборки для общего доступа
Global Assembly Cache (GAC)
C:\WINDOWS\Assembly
Дополнительная информация о версии
– общее или сильное имя
Слайд 17«Сильные» имена сборок
Дружественное текстовое имя и «культурная информация»
Идентификатор версии
Пара открытый/закрытый
ключ
Цифровая подпись
Слайд 18Текст сборки
using System;
using System.Windows.Forms;
namespace SharedAssembly
{
public class VWMiniVan
{
public VWMiniVan(){}
public void Play60sTunes()
{
MessageBox.Show("What a loooong, strange trip it's been...");
}
private bool isBustedByTheFuzz =
false;
public bool Busted
{
get { return isBustedByTheFuzz; }
set { isBustedByTheFuzz = value; }
}
}
}
Слайд 19Создание пары открытый/закрытый ключ
Слайд 20Установка сборки в GAC
Утилита gacutil.exe
Перетащить мышкой
Слайд 21Использование общей сборки в приложении
namespace SharedLibUser
{
using System;
using SharedAssembly;
public class SharedAsmUser
{
public
static int Main(string[] args)
{
try
{
VWMiniVan v = new VWMiniVan();
v.Play60sTunes();
}
catch(TypeLoadException e)
{
// Не
могу найти сборку!
Console.WriteLine(e.Message);
}
return 0;
}
}
}
Слайд 22Анатомия версии сборки
Номер основной версии (Несовместимые)
Номер дополнительной версии (Несовместимые)
Номер редакции
(Возможно совместимые)
Номер сборки (Quick Fix Engineering)
Слайд 24Загрузка разных версий сборок
По умолчанию: первые две цифры запрашиваемые, вторые
– последние по номеру
Явная загрузка нужной версией: управление из файла
*.config
publicKeyToken="6c0646f072c6fe39"
culture=""/>
newVersion="2.0.0.0"/>
Слайд 25Домены приложения
Процесс состоит из одного или нескольких доменов
В рамках домена
работает один или несколько потоков
Домен приложения полностью изолирует используемые в
его рамках ресурсы от других доменов (за исключением удаленного доступа к данным)
Слайд 27Работаем с доменами приложения
namespace MyAppDomain
{
using System;
using System.Windows.Forms;
// Это пространство имен требуется для работы с типом Assembly
using System.Reflection;
public class MyAppDomain
{
public static void PrintAllAssemblies()
{
// Получаем список всех загруженных в текущий домен приложения сборок
AppDomain ad = AppDomain.CurrentDomain;
Assembly[] loadedAssemblies = ad.GetAssemblies();
Console.WriteLine("Here are the assemblies loaded in " + "this appdomain\n");
// Теперь выводим полное имя для каждой сборки
foreach(Assembly a in loadedAssemblies)
Console.WriteLine(a.FullName);
}
public static int Main(string[] args)
{
// Производим принудительную загрузку System.Windows.Forms.dll
MessageBox.Show("Loaded System.Windows.Forms.dll");
PrintAllAssemblies();
return 0;
}
}
}
Слайд 28Пространство имен System.Threading
Interlocked – синхронизация общего доступа к данным
Monitor –
синхронизация потоковых объектов
Mutex – примитив синхронизации
Thread – поток, работающий в
.NET
ThreadPool – управление набором взаимосвязанных потоков
Timer – определяет делегат, который будет вызван в указанное время
WaitHandle – представляет все объекты синхронизации
ThreadStart – делегат со ссылкой на метод, который должен быть вызван перед запуском потока
TimerCallback – делегат для объектов Timer
WaitCallback – делегат для рабочих элементов ThreadPool
Слайд 31Запуск вторичных потоков
// вспомогательный класс
internal class WorkerClass
{
public void DoSomeWork()
{
// Выводим
на консоль информацию о рабочем потоке
Console.WriteLine("ID of worker thread is:
{0}",
Thread.CurrentThread.GetHashCode());
// Выполняем некоторые действия
Console.Write("Worker says: ");
for (int i = o; i < 10; i++)
{
Console.Write(i+ ", ");
}
Console.WriteLine();
}
}
Слайд 32Запуск вторичных потоков
using System.Threading;
public class MainClass
{
public static int Main(string[] args)
{
// Выводим на консоль информацию о текущем потоке
Console.WriteLine("ID of primary thread is: {0}",
Thread.CurrentThread.GetHashCode());
// Создаем объект класса WorkerClass
WorkerClass w = new WorkerClass();
// А теперь создаем и запускаем фоновый поток
Thread backgroundThread =
new Thread(new ThreadStart(w.DoSomeWork));
backgroundThread.Start();
return 0;
}
}
Слайд 33Именованные потоки
using System.Threading;
public class MainClass
{
public static int Main(string[] args)
{
// Присваиваем имя текущему потоку
Thread primaryThread = Tread.CurrentThread;
primaryThread.Name = "Boss man";
Console.WriteLine("Id of {0} is {1}", primaryThread.Name,
promaryThread.gethascode());
// …
return 0;
}
}
Слайд 34Параллельная работа потоков
public static int Main(string[] args)
{
Console.Write("Do you
want [1] or [2] threads? ");
string threadCount =
Console.ReadLine();
// Создаем объект класса WorkerClass
WorkerClasss w = new WorkerClass();
// Создаем дополнительный поток, только если это указано
if(threadCount = = "2")
{ // Создаем дополнительный поток
Thread backgroundThread = new Thread(new ThreadStart(w.DoSomeWork));
backgroundThread.Start();
}
else
w.DoSomeWork();
// Даем первичному потоку свое задание
MessageBox.Show("I'm busy!");
return 0;
}
Слайд 35Как «усыпить» поток
internal class WorkerClass
{
public void DoSomeWork()
{
// Выводим
информацию о рабочем потоке
Console.WriteLine("ID of worker thread is: {0}",
Thread.CurrentThread.GetHashCode());
//
Делаем работу "с перекурами"
Console.Write("Worker says: ");
for(int i = 0; i < 5; i++)
{
Console.WriteLine(i + ", ");
Thread.Sleep(5000);
}
Console.WriteLine();
}
}
Слайд 36Одновременный доступ к данным из разных потоков
public class MainClass
{
public
static int Main(string[] args)
{
// Создаем единственный объект класса WorkerClass
WorkerClass
w = new WorkerClass();
// Создаем три отдельных потока, каждый из которых производит вызов
// к одному и тому же объекту
Thread workerThreadA = new Thread(new ThreadStart(w.DoSomeWork));
Thread workerThreadB = new Thread(new ThreadStart(w.DoSomeWork));
Thread workerThreadC = new Thread(new ThreadStart(w.DoSomeWork));
// Теперь запускаем все три потока
WorkerThreadA.Start();
WorkerThreadB.Start();
WorkerThreadC.Start();
return 0;
}
}
Слайд 37Ключевое слово lock
internal class WorkerClass
{
public void DoSomeWork()
{
// Только один поток в конкретный момент времени сможет
// выполнять этот код!
lock(this)
{
// Делаем все ту же работу
for(int i=0; i < 5; i++)
{
Console.WriteLine("Worker says: {0},", i);
}
}
}
}
Слайд 38Использование System.Threading.Monitor
internal class WorkerClass
{
public void DoSomeWork()
{
//
Определяем элемент для мониторинга в целях синхронизации
Monitor.Enter(this);
try
{
//
Выполнить работу...
for(int i = 0; i < 5; i++)
{
Console.WriteLine("Worker says: {0},", i);
}
}
finally
{
// Была ошибка или нет, а из монитора придется выйти
Monitor.Exit(this);
}
}
}
Слайд 39Опасность одновременного изменения переменной
public class IHaveNoIdea
{
private long refCount = 0;
public
void AddRef()
{ ++refCount; }
public void Release()
{
if(--refCount == 0)
{
GC.Collect();
}
}
}
Слайд 40Применение System.Threading.Interlocked
public class IHaveNoIdea
{
private long refCount = 0;
public void AddRef()
{
Interlocked.Increment(ref
refCount);
}
public void Release()
{
if (Interlocked.Decrement(ref refCount) ==
0)
{
GC.Collect();
}
}
}