Интернет-журнал "Домашняя лаборатория", 2007 №6
Шрифт:
[Synchronization(0x00000008)]
Задание данного атрибута означает, что компонент News будет располагаться в новом контексте (3), причем этот контекст образует и новый домен синхронизации. Компоненты Account и Tax должны располагаться в различных контекстах (1 и 2), но эти контексты должны входить в один домен синхронизации.
Для проверки этих утверждений внесем еще несколько дополнений в код сервера:
…….
namespace SPbU.AOP_NET {
……
public class Tax: ContextBoundObject {
…….
public Tax {
…….
Console.WriteLine {
"Tax syncProperty == News syncProperty "+
Object.ReferenceEquals(_syncProperty,
_news.syncProperty));
……
}
…….
}
…….
public class News: ContextBoundObject {
private SynchronizationAttribute _syncProperty;
public News {
_syncProperty =
(SynchronizationAttribute)
Thread.CurrentContext.GetProperty {
"Synchronization");
……
}
……
internal SynchronizationAttribute syncProperty {
get { return _syncProperty;}
}
…….
}
Просматривая
Server is listening
News context = 3 News constructor thread = 9 IsPoolThread = True
Tax syncProperty == News syncProperty False
Tax context = 2 Tax constructor thread = 9 IsPoolThread = True
Account context = 1 Account constructor thread = 9 IsPoolThread = True
Account syncProperty == Tax syncProperty True
…….
И последнее замечание касается файла LogFile. Просматривая его, можно заметить, что теперь перехватываются все вызовы, идущие к компонентам Tах и News, идущие не только от компонента Account, но и от компонента Tах к компоненту News. Это объясняется тем, что все компоненты живут в различных контекстах и все вызовы пересекают границу контекста и, следовательно, перехватываются.
Еще раз про атрибут синхронизации
Эта глава продолжает изучение кода атрибута синхронизации из Rotor, рассмотрение которого было начато в предыдущей главе. Там мы рассмотрели основные механизмы, связанные с определением контекста и домена синхронизации, в которых будет размещен новый объект — экземпляр класса, которому приписан атрибут SynchronizationAttribute. Это конструкторы (четыре варианта) и методы IsContextOK и GetPropertiesForNewContext класса SynchronizationAttribute. Теперь мы сосредоточимся на самом алгоритме синхронизации и попутно рассмотрим несколько важных понятий, связанных с программированием в CLR.
Инициализация свойства синхронизации в домене синхронизации
Начнем с метода InitIfNecessary класса SynchronizationAttribute:
internal virtual void InitlfNecessary {
lock(this) {
if (_asyncWorkEvent == null) {
_asyncWorkEvent = new AutoResetEvent(false);
_workltemQueue = new Queue;
_asyncLcidList = new ArrayList;
WaitOrTimerCallback callBackDelegate =
new WaitOrTimerCallback(this.DispatcherCallBack);
ThreadPool.RegisterWaitForSingleObject {
_asyncWorkEvent,
callBackDelegate,
null,
_timeOut,
false);
}
}
}
Данный метод вызывается при формировании каждого нового контекста синхронизации, однако делает он что-либо только в том случае, когда этот контекст начинает собой новый домен синхронизации. В этом случае инициализируется свойство синхронизации данного контекста, которое одновременно будет и свойством синхронизации всего домена синхронизации. При включении в этот домен синхронизации нового контекста синхронизации новое свойство синхронизации не создается и его инициализация не требуется.
В коде данного метода мы сталкиваемся с рядом ранее не рассмотренных понятий, таких как критические секции, делегаты, пул рабочих потоков, события. Прежде чем двигаться дальше, рассмотрим упомянутые понятия.
Критические секции
Если атрибут синхронизации позволяет управлять синхронизацией декларативно, то критическая секция обеспечивает решение данной проблемы в рамках парадигмы процедурного программирования. И судя по всему, более эффективно, так как при этом нет необходимости создавать контексты, перехватчики, преобразовывать вызовы в сообщения и обратно из сообщение формировать вызовы.
Рассмотрим несколько примеров.
Ранее мы уже рассматривали консольное серверное приложение MyServer, поддерживающее некоторый банковский счет. Клиентские приложения могли параллельно делать вклады на этот счет. Синхронизация обеспечивалась за счет использования атрибута синхронизации SynchronizationAttribute, который приписывался классу Account, и наследования этого класса от класса ContextBoundObject.
Теперь мы обеспечим синхронизацию за счет использования критических секций.
Простейший способ связан с приписыванием методу Add атрибута [MethodImpl (MethodImplOptions.Synchronized)]. Данный атрибут запретит вод в тело метода Add какого-либо потока, если этот метод уже выполняется в другом потоке. В данном случае мы полностью полагаемся на компилятор, который должен обеспечить требуемую функциональность.
…….
namespace MyServer {