Несмотря на определенные различия между CLR из .NET и SSCLI, код из CLI является полезным источником информации при изучении .NET. В частности, изучение кода атрибута SynchronizationAttribute из SSCLI позволяет глубже понять его семантику, что необходимо для правильного и эффективного использования данного атрибута в .NET.
В последующих разделах данной главы технология работы с контекстами и пользовательскими атрибутами будет продемонстрирована на простом примере, который является некоторым расширением ранее рассмотренного примера из предыдущей главы.
Описание примера
Изучаемый ниже пример разрабатывался с целью демонстрации ряда тонких моментов, связанных с контекстами, потоками и пользовательскими атрибутами. На основе использования этого примера мы проведем серию экспериментов, которые помогут прояснить излагаемые вопросы.
Как и в предыдущем разделе консольное клиентское приложение делает вклад на счет, поддерживаемый другим ранее запущенным консольным серверным приложением. Серверное приложение содержит три компонента, представляемые классами Account, Tax и News.
Компонент Account поддерживает счет, позволяя клиентам сделать вклад (метод Add) и узнать величину текущего счета (метод Total).
Компоненты Tax и News представляют соответственно налоговую службу и агентство новостей. Оба компонента получают уведомления о вкладах на счет (метод Notify) и выводят соответствующую информацию на консоль.
Компонент Tах активируется компонентом Account, который добровольно информирует Tax о каждом сделанном вкладе в процессе выполнения вызова метода Add. В свою очередь компонент Tах активирует компонент News, которому и передает полученную от Account информацию для придания ее гласности. Не полагаясь исключительно на компонент Tах, компонент Account получает от Tах ссылку на компонент News и самостоятельно напрямую посылает уведомление этому компоненту.
Для демонстрации пользовательских атрибутов, позволяющих задавать набор сервисов, доступных компонентам серверного приложения, мы будем использовать два атрибута:
• SynchronizationAttribute
Этот атрибут синхронизации реализован в .NET и уже применялся в примере предыдущей главы. Как уже отмечалось выше, семантика этого атрибута весьма не проста, и для ее полного понимания полезно познакомиться с кодом этого атрибута, представленным в SSCLI (файл sscli clr scr bcl system runtime remoting synchronizeddispatch.es). Этот код содержит 1010 строк (вместе с комментариями). Мы не будем здесь его разбирать полностью, однако основные сведения, полученные при его изучении, будут в данной главе изложены и продемонстрированы в экспериментах.
• MyCallTraceAttribute
Код этого атрибута трассировки вызовов приводится в данной главе. Надо заметить, что атрибуты с семантикой трассировки вызовов очень популярны среди авторов, пишущих на темы .NET. Приведенный здесь код содержит фрагменты из ряда опубликованных примеров (в частности, из кода к статье [АОРЗ]) и используется исключительно в демонстрационных целях.
Весь код примера содержится в трех файлах:
• MyApp.cs
Этот файл содержит код консольного клиентского приложения.
• MyServer.cs
Файл содержит код консольного серверного приложения и трех компонентов (Account, Tax, News).
• MyCallTrace.cs
Этот файл содержит код атрибута трассировки вызовов.
Ниже приводится makefile, который можно использовать для компиляции и сборки клиентского и серверного приложений
all: МуАрр MyServer
clean:
@del МуАрр. exe MyServer.exe
МуАрр: МуАрр. ехе
МуАрр. exe: MyApp.cs MyServer.exe
csc /r: MyServer.exe MyApp.cs
MyServer: MyServer.exe
MyServer.exe: MyServer.cs MyCallTrace.cs
csc MyServer.cs MyCallTrace.cs
Из этого кода видно, что и клиентское, и серверное приложение являются приложениями типа .ехе и запускаются в различных доменах приложений. Для использования этого makefile достаточно запустить nmake, в результате чего будут получены файлы МуАрр. ехе и MyServer.ехе.
Серверное приложение
Ниже приводится код из файла MyServer.cs, который является некоторым расширением одноименного файла, рассмотренного в предыдущей главе.
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Threading;
using System.Runtime.Remoting.Contexts;
namespace SPbU.AOP_NET {
public interface IAccumulator {
void Add(int sum);
}
public interface IAudit {
int Total();
}
[Synchronization()]
[MyCallTrace("LogFile")]
public class Account: ContextBoundObject,
IAccumulator, IAudit {
private Tax _tax;
private int _sum = 0;
public Account() {
_tax = new Tax ();
Console.WriteLine("Account context = " +
Thread.CurrentContext.ContextID + "\n" +
"Account constructor thread = " +
Thread.CurrentThread.GetHasheode() +
" IsPoolThread = " +
Thread.CurrentThread.IsThreadPoolThread);
}
public void Add(int sum) {
_sum += sum;
_tax.Notify("new Account operation: +" + sum); _
_tax.news.Notify("direct notification from Account");
Console.WriteLine("Account Add thread = " +
Thread.CurrentThread.GetHasheode() +
" IsPoolThread = " +
Thread.CurrentThread.IsThreadPoolThread);
}
public int Total() {
return _sum;
}
}
[Synchronization()]
[MyCallTrace("LogFile")]
public class Tax: ContextBoundObject {
private News _news;
public Tax() {
_news = new News();
Console.WriteLine("Tax context = " +
Thread.CurrentContext.ContextID + "\n" +
"Tax constructor thread = " +
Thread.CurrentThread.GetHasheode() +
" IsPoolThread = " +
Thread.CurrentThread.IsThreadPoolThread);
}
public void Notify(String msg) {
Console.WriteLine("Tax notification: " + msg);
Console.WriteLine("Tax Notify thread = " +
Thread.CurrentThread.GetHasheode() +
" IsPoolThread = " +
Thread.CurrentThread.IsThreadPoolThread);
_news.Notify(msg);
}
public News news {
get {
return _news;