Под событием, в данном случае, понимается способ оповещения сервером заинтересованной стороны (клиентов) об изменении своего состояния. Для того, чтобы клиент мог обрабатывать событие, он должен создать связь между объектом события и обработчиком. В качестве обработчиков вуступают методы объектов, т.е. обработчик задается указателем на объект и указателем на метод. С одним событием м.б. связано несколько обработчиков. При генерации события указываются аргументы, которые получат обработчики события (т.е. методы). В процессе генерации события быдут вызваны все обработчики с указанными аргументами.
Класс Cntm::DeferEvent является шаблоном. В качестве параметра шаблона указывается сигнатура функции (т.е. без указания конкретного класса обработчика), которая будет использоваться при генерации события. Пример объвления события: DeferEvent<void (int,const string&,Class1*,const Class2::Ptr&)> ChangeEvent; Методы обработчиков должны иметь такую же сигнатуру, как и событие. При попытке связать событие с собработчиком с другой сигнатурой будет выдана ошибка компиляции, т.е. обеспечивается жесткая проверка типов. Возвращаемое значение (если его тип не void) при вызове обработчиков игнорируется. Обычно объект события объявляется как public поле объекта, генерирующего событие.
Свзывание события с обработчиком осуществляется с помощью специального класса Cntm::EventLink (не является шаблоном). Что бы создать связь нужно создать объект этого класса и указать событие и обработчик (указатель на объект, указатель на метод и дополнительные параметры). При уничтожении этого объекта связь будет автоматически разорвана. Также у этого класса есть метод Attach(), позволяющий изменить связь и имеющий такие же аргументы, что и конструктор (при этом старая связь разрывается и создается новая), а также метод Detach(), разрывающий текущую связь. Рассмотрим пример: EventLink onChangeClass1(object1->ChangeEvent, this, &ThisClass::OnChangeClass1); onChangeClass1.Attach(object2->ChangeEvent, this, &ThisClass::OnChangeClass2); onChangeClass1.Detach();
Генерация события производится с помощью оператора operator() объекта события. В качестве аргументов этого оператора указываются аргументы, которые будут переданы обработчикам. Пример: onChangeClass1(45, "Привет всем обработчикам", this, object2);
После общего обзора перейдем к рассмотрению различных аспектов.
В названии класса говорится, что это отложенное (сериализованное) событие. Это значит, что обработчики события будут вызваны не в момент генерации события (т.е. не в операторе operator()), а когда-то потом. Зачем "потом"? Концепция обработки событий подразумевает, что клиенты, обрабатывающие события, знают о свойствах и поведении сервера, в то время, как сервер не знает о поведении обработчиков своих событий в клиентах (по крайней мере, если проектировать сложные системы так, чтобы сервер знал о поведении обработчиков, то это еще больше усложнит процесс проектировки и снизит гибкость при доработке). Например, если событие генерируется в какой-то нестабильной точке алгоритма, то обработчик может вызвать такой метод сервера, выполнение которого приведет к ошибкам в данных сервера, т.к. произойдет реентерабельный вход в объект, который, возможно, не был предусмотрен. Т.о. при прямой генерации событий нарушается естественный порядок выполнения алгоритма. Что бы избежать проблем, следует четко определять безопасные места генерации событий, применять спец. средства борьбы с реентерабельным входом или просто запрещать вызовы определенных методов из обработчиков. Отложенные события лишены этого недостатка, они не нарушают естественного хода выполнения алгоритма, т.к. в момент генерации происходит только фиксация аргументов события, а вызов обработчиков произойдет после завершения алгоритма. Однако, за все надо платить. Недостатком отложенных событий является то, что между моментом генерации и моментом начала обработки проходит некоторое время, за которое состояние сервера может измениться. Одним из способов борьбы с этим недостатком является передача состояния в качестве аргументов события. Т.о. если для обработки события нужно текущее состояние сервера, а сам обработчик не меняет текущее состояние сервера, то можно использовать обычные события (назовем их прямыми), в противном случае рекомендуется использовать отложенные события (которые имеют и дополнительные плюсы в многопоточных системах).
В названии класса сказано, что это синхронное событие, это значит, что оно тесно связано с понятием синхропространства (ознакомтесь с документацие по классу Cntm::SynchroSpace). Каждый обработчик события выполняется внутри определенного синхропространства (которое указывается тем или иным способом при свзывании события с обработчиком), вход в которое произведен в реентерабельном режиме. Если событие сгенерировано в некотором синхропространстве, то гарантируется, что вход в это синхропространство из обработчиков события будет произведен только после выхода из него потока выполнения алгоритма, который сгенерировал событие, причем, не имеет значение, в каком синхропространстве выполняется обработчик (в этом-же или другом). Т.о. обеспечивается защита от реентерабельного входа в объект, сгенерировавший событие (т.к. вход невозможен во все синхропространство, к которому принадлежит объект). Это правило имеет исключение, когда поток выполнения алгоритма вошел в синхропространство в реентерабельном режиме и произвел реентерабельный вход в синхропространство. В этом случае обработчики могут произвести свой вход в синхропространство в момент реентерабельного входа. Т.о. ход выполнения алгоритма может быть нарушен, но только при жестко определенных условиях: режим входа в синхропространство - реентерабельный и выполнение реентерабельного входа в синхропространство. Еще раз подчеркнем, что обработчик выполняется не в том синхропространстве, в котором сгенерировано событие (оно м.б. сгенерировано и вне синхропространства), а в указанном при связывании.
Рассмотрим пример системы, использующей библиотеку QT (см. документацию по этой библиотеке). Для создания главного синхропространства, интегрированного с QT следует мспользовать класс Cntm::QTMainSynchroSpace (см. документацию по этому классу), прочих синхропространств в системе нет. Приложение выполняется, когда запущен цикл обработки сообщений (метод QApplication::exec()). Будем считать реентерабельным входом в главное синхропространство отображение модальных окон, т.е. когда происходит запуск вложенного цикла обработки сообщений. В этом случае если где-то (в том числе и не в главном потоке) будет сгенерировано событие, то обработчики будут выполнены синхронно с остальными событиями QT, через цикл обработки событий в главном потоке, причем после того, как произойдет возврат в цикл обработки. Т.е. обработка отложенных событий ни чем не отличается от событий QT, таких, как событие нажатия кнопки или событие таймера.
Подводя итог всем этим обяснениям, можно сказать, что обработчик отложенного события всегда выполняется изолированно от генерации события. В каком бы потоке не было бы сгенерировано событие, обработчик всегда будет вызываться синхронизированно, в частности, если обработчик связан с главным синхропространством QT, то его вызов всегда будет синхронизирован с другими событиями QT (конкретнее, он будет выполнен в цикле обработки событий QT).
Теперь подробнее о связях событий с обработчиками. Класс Cntm::EventLink имеет конструктор по умолчанию, который инициализирует пустую связь. Конструкторы связывания и методы Attach() имеют 3 обязательных параеметра и 1 опциональный. Рассмотрим их:
Время жизни объектов и подсчет ссылок. При создании связи между событием и обработчиком происходит увеличение кол-ва ссылок на синхропространство, в котором производится обработка. Т.о. связи событий могут удерживать синхропространства, поэтому для нормального завершения работы необходимо уничтожить все связи. Рекомендация: наиболее целесообразно создавать связи как поля класса, который содержит обработчики событий. В этом случае вместе с объектом будут разорваны и все связи для объекта. На объект, обрабатывающий событие (если это объект с подсчетом ссылок, наследующий Cntm::IRefObject) кол-во ссылок не увеличивается (что бы не препятствовать его удалению). Если после создания связи и до ее разрыва произойдет уничтожение объекта события, ничего страшного не случится, просто событие больше никогда не будет сгенерировано. Если во время существования связи произойдет уничтожение объкта обработчика, то это приведет к ошибкам выполнения, поэтому время жизни связи не должно превышать время жизни объекта обработчика.
Процесс генерации события. При генерации вызываются только те обработчики, которые были связаны с событием до генерации, гарантируется, что обработчики добавленные после генерации, вызваны не будут. Если связь разрушена после генерации события, но до вызова обработчика, то гарантируется, что обработчик вызван не будет. Для объектов обработчиков, являющихся объектами с подсчетом ссылок: если на момент вызова обработчика события на объект не осталось ссылок, но он еще существует (находится в удаляемом состоянии, см. Cntm::IRefObject), то вызов обработчика произведен не будет, если вызов обработчика произведен, то гарантируется наличие хотя бы одной ссылки на объект обработчика до выхода из метода обработки. Исключения, сгенерированные в обработчиках, ни как не влияют на вызов других обработчиков, они просто игнорируются. В процессе вызова обработчиков имеется только одна копия сохраненных аргументов события, поэтому: 1. изменения аргументов коснутся всех обработчиков, которые будут вызваны после изменившего; 2. обработчики могут быть вызваны одновременно в разных потоках, поэтому и доступ к одному и тому же значению аргумента может производиться из разных потоков.
Данный класс обеспечивает многопоточность.
См. определение в файле DeferEvent.h строка 65
Друзья | |
class | SignatureArgsCollector< DeferEvent< SignatureT >, void, SignatureInfo< SignatureT >, SignatureInfo< SignatureT >::argsCount > |
Классы | |
class | SignatureArgsCollectObject |
Класс объекта вызова. |
friend class SignatureArgsCollector< DeferEvent< SignatureT >, void, SignatureInfo< SignatureT >, SignatureInfo< SignatureT >::argsCount > [friend] |
См. определение в файле DeferEvent.h строка 69
© Овсеевич Р.В. Документация по CntmLib 1.1.4 от 28 May 2008. Создано системой 1.5.3 |