1 . НемедЛенный запуск новой ВОДЯНОЙ струи. Запуск новой струи в тот момент, когда одна водяная струя уже приведена в действие, приводит к такому же эффекту, что и более продолжительное по сравнению с нормальным дейст вие уже существующей водяной струи. ОптимальнаядЛительность нормаль ной струи должна быть определена экспериментально. Более продолжи тельная водяная струя может привести к излищнему расходу воды, поэтому этот вариант не очень хорош.
2.Ждать, пока не закончится одна водяная струя, а затем немедЛенно запус тить другую. Такой вариант на самом деле вдвое увеличивает продолжи тельность водяной струи, поэтому предполагает еще более значительный расход воды, чем первый вариант.
3.Ничего не делать. Этот вариант не предусматривает излищнего расхода воды, поэтому он является наилучшим.
Если существуют два параллельных обращения к методу f lush, то удачной стратегией считается разрешение выполнения одного вызова и игнорирование
другого.
Предположим, что обращение к методу объекта производится в тот момент, когда состояние объекта не позволяет ему правильно выполнить метод. Если управление ситуацией таково, что метод завершается, не выполнив свою задачу, значит, была произведена отмена метода. В UML нет стандартного способа, позволяющего обозначить метод, вызов которого сопровождается его отменой.
вВ данной книге вызов метода, сопровождающегося отменой, обозначается
виде стрелки, изогнутой в обратном направлении.
На рис. 9. ] 5 представлено такое поведение метода flush класса Flusher.
Глава 9. Шаблоны проектирования для конкурирующих операций
РЕШЕНИЕ
Диаграмма взаимодействия, представленная на рис. 9. 16, включает объекты, сотрудничаюшие в рамках шаблона Balking.
Объект C l ient вызывает метод doI t объекта Service. Изогнутая в обратном
направлении стрелка1 указывает на тосе, что может быть произведена отмена ме
тода. Если метод do t объекта Se rvi вызывается в тот момент, когда состоя ние объекта Service не позволяет выполнить обрашение к его методу dolt, то
этот метод возвращается, не выполнив свои обычные функции.
Метод dol t возвращает результат, обозначенный на диаграмме как didIt.
Этот результат может иметь значение true или false в зависимости от того, вы полнил ли он свои обычные функции или бьmа произведена отмена метода.
l:didIt:-dоIt()
Рис. 9.16. Взаимодействие, демонстрирующее отмену метода
РЕАЛИЗАЦИЯ
Если можно выполнить отмену eToдa, то прежде всего он, как правило, прове ряет состояние объекта, которому он принадлежит, с целью определения, дол жен ли он быть выполнен.
Когда метод объекта выполняется, решив, что не будетдляотменен, недопустимо, чтобы состояние объекта стало вдруг неприемлемым выполнения этого ме
тода. Чтобы зашититься от подобной несогласованности, можно использовать
шаблон Single Threaded Execution.
Вместо того чтобы сообшить о своей отмене всем, кто его вызывал, посредст
вом возврата некоторого значения, метод может также известить их о том, что
была не произведена отмена, посредством генерации исключительной ситуа
ции. Если вызывающую сторону не интересует, была ли произведена отмена
метода, он может не возврашать такую информацию. СЛЕДСТВИЯ
способ управления обращениями к методам объектов, состояние которых не
позволяет выполнить эти обращения.
Single Тhreaded Execution. Шаблон Balking часто используется вместе с шабло ном Single Threaded Execution для согласования изменений состояния объекта.
Этот шаблон основан на материале, представленном в работе [Lea97].
СИНОПСИС
Управляет порядком, в соответствии с которым потокам предстоит выполнить последовательный код, используя для этой цели объект, который явным обра зом задает последовательность ожидающих потоков. Шаблон Scheduler обеспе чивает механизм реализации политики планирования, но при этом не зависит ни от одной конкретной политики.
КОНТЕКСТ
Предположим, проектируется ПО ДЛЯ управления физической безопасностью зданий. Система безопасности должна включать контрольные пункты. Прежде чем пройти через такой пункт, человек должен провести идентификационной карточкой через сканер. При этом контрольный пункт либо разрешает человеку пройти, либо нет. В любом случае: прошел ли человек через контрольный пункт, или его карточка не была принята - входные данные будут распечатаны вжурнале регистрации, находящемся в центральном офисе службы безопасности.
Взаимодействия на рис. 9.17 включают объекты Securi tyCheckpoint, которые создают объекты Journa lEntry и передают их методу print объекта Printer. Несмотря на простотудиаграммы, с этой структурой связана одна проблема. Она возникает в том случае, когда люди проходят через три или более пропускных
пункта примерно в одно и то же время. Когда принтер распечатывает первую за
пись регистрации, другие обращения к принтеру находятся в состоянии ожида
ния. После окончания распечатки первой регистрационной записи неизвестно,
какая запись будет распечатана следующей. Это означает, что записи регистра
ции могут распечатываться не в том порядке, в котором контрольные пункты безопасности передавали их на принтер.
h kpoin
t-------'- ---"'-------"
1._____ __....J
---+
1 :рп пЦentry) {guarded}
Р
:SecurityC ec t
:
d t
п _е r
1 1:entry:..createo
entrv:Journa {new}
lEntryзаимод ствие АЛЯ ж рнала с стем безо
Рис. 9.17. В ей у и ы пасности
474 • Глава 9. Шаблоны проектирования ДЛЯ конкурирующих операций
Чтобы записи в журнале распечатывались в порядке их фактического ПОСТУпле_
ния, можно просто помещать каждую журнальную запись в очередь и затем
распечатывать журнальные записи в том порядке, в котором они заносились
в очередь. Хотя здесь все еще возможно одновременное поступление трех или
более журнальных записей, вероятность такой ситуации значительно СНИжает_
ся. Для распечатки журнальной записи может потребоваться одна сеКУнда.
Чтобы возникла проблема, необходимо, чтобы две другие журнальные записи
поступили в течение этого промежутка времени. Помещение журналЬной запи
си в очередь может потребовать всего лишь примерно одной микросекунды.
При этом вероятность нарушения порядка распечатки журнальных записей
уменьшается в миллион раз.
Класс Printer мог бы отвечать за организацию очереди журнальных записей,
подлежащих выводу на печать. Однако помещение в очередь обращений к ме тодам, подлежащих последовательному выполнению, - это характеристика,
которая обладает большим потенциалом с точки зрения многократного ис
пользования, если она реализуется как отдельный класс. Представленная на рис. 9. 18 диаграмма взаимодействия демонстрирует, как объект принтера мо жет взаимодействовать с другими объектами, организуя очередь на выполнение обращений к его методу print.
,.-------:SecurityCheckp1
2:
oint
рппt(епtry)
l:create()1
entry:JoцrnalEntry
Этот пор, ему
1 2.1:enter(entry)
2.2:done() 1
метод не возвратится до тех
пока Scheduler не разрешит
это сделать.
Рис. 9.18. Журнал системы безопасности, использующий объект-планировщик
Объект SecurityCheckpo int вызывает метод print объекта printer. Метод print начинается с обращения к методу enter объекта Scheduler. Метод enter не заканчивает свою работу до тех пор, пока объект Schedu ler не разре
шит ему это сделать. Когда метод print заканчивает свою работу, он вызывает
метод done объекта Scheduler. В промежутке времени между возвратом мето
да enter И вызовом метода done объект Scheduler полагает, что управляемый
им ресурс занят. Ни одно из обрашений к его методу enter не будет выполне
но, пока объект Scheduler считает, что управляемый им ресурс занят. Это га
рантирует, что в некоторый момент времени только один поток выполняет ту часть метода print, которая начинается с его обращения к методу enter объ
екта Scheduler и заканчивается его обращением к методу done объекта
Scheduler.
Если объект Scheduler не ожидает обращения к своему методу done, то вызов его метода enter будет выполнен немедленно. Затем объект Sche
Scheduler • 475
Реальная логика выполнения, согласно которой объект Schedu ler принимает решение, когда должен выполниться вызов метода enter, инкапсулирована
в объекте Scheduler. Это позволяет менять логику, не затрагивая другие объ екты. В данном примере, когда несколько обращений к методу enter ожидают
выполнения• , можно применить следующую логику управления.
•duler будет ожидать вызова своего метода done .
Если объект Scheduler ожидает обращения к своему методу done, то вызов его метода enter будет ждать своего выполнения до тех пор, пока не про изойдет вызов метода done объекта Scheduler. Если при вызове метода done объекта Scheduler имеются какие-либо обращения к его методу еn te r, ожидающие выполнения, то для выполнения будет выбрано одно из
•таких обращений к методу enter.
Если своего выполнения ожидают несколько обращений к методу enter объекта Schedu ler, то объект Scheduler должен выбрать следующий вы зов метода enter, которому будет разрешено выполниться. Он выберет тот, который был передан объекту JournalEntry раньше всего. Если несколько объектов JournalEntry имеют одинаковое время поступления, то один из них будет выбран произвольно.
Чтобы класс Scheduler мог сравнивать время создания объектов Journal Entry и оставаться при этом независимым от класса Journal Entry, он не дол жен прямо ссьшаться на класс JournalEntry. Он может ссылаться на класс Journa lEntry через интерфейс (рис. 9. 19).
*
«interface»
ScheduleOrdering
....
scheduleBefore(:ScheduleOrdering):boolean
Планирует обработку
06'ЬеКТОВ
ScheduleOrdering
*
I
I
Распечатывает
1
I
I JournalEntry
*
I
Scheduler
I
Соэдает....*
1
....Планирует распечатку
1
1
1
журнальных записей
I SecurityCheckpoint
Испольэует
регистрации
*
1
Printer
I
Рис. 9.19. Классы планирования регистрационных записей в журнале
I<.ласс Schedu ler ничего не знает о классе JournalEnt ry. Он просто плани
Рует обработку объектов, реализующих интерфейс ScheduleOrdering. Этот
интерфейс объявляет метод scheduleBefore, который вызывается классом
476
• Глава 9. Шаблоны проектирования ДЛЯ конкурирующих операций
Scheduler С целью определения, какой из двух объектов ScheduleOrdering
должен быть обработан первым. Хотя класс Scheduler инкапсулирует ПОЛИти
ку управления, позволяющую определять, когда объекту ScheduleOrdering
будет дано разрешение на обработку, он делегирует принятие решения, касаю
Шаблон Sclledu1er использует некоторый объект, чтобы явным образом управлять параллельными запросами с целью их непараллельной обработки. На рис. 9.20 показаны классы и интерфейс в шаблоне Scheduler.
*
«;"terface»
• Планирует обработку
ScheduleOrderi"9
объектов ScheduleOrderlng
scheduleBefore(:ScheduleOrderi"9):boolea"
1
А
I Scheduler
I
I
I
1
АЛЯ
I
1
I
• Планирует объекты
I
I
Обрабатывает
Request
обработки
I
I
Request
l
Processor
I
1 *
1
'1
Рис. 9.20. Классы шаблона Scheduler
Опишем роли, исполняемые этими классами и интерфейсом.
Request. Выступающие в этой роли классы должны реализовывать интерфейс,
исполняющий роль ScheduleOrdering. Объекты Request инкапсулируют за
прос к объекту Proces sor на выполнение каких либо действий.
Processor. Экземпляры классов в этой роли выполняют вычисление, описанное
в объекте Reque st. Экземпляры классов могут быть представлены нескольКИ ми объектами Reques t, но они могут выполнить только одну операцию, пред
ставленную объектом Request, в некоторый момент времени. Объект p roces : sor делегирует объекту Scheduler ответственность за управление обрабоТКОIi
объектов Request. В какой то момент должен обрабатываться только одИК
объект.
s chedu leBefore.
Scheduler - 477
Scheduler. Экземпляры классов в этой роли управляют обработкой объектов Reques t, выполняемой объектом Proces sor. Чтобы быть независимыми от типов запросов, класс Schedu ler не должен ничего знать об управляемом им
классе Request. Вместо этого он осуществляет доступ к объектам Reques t че рез реализуемый ими интерфейс ScheduleOrder ing.
Класс в этой роли отвечает за принятие решения о том, когда будет выполнен следующий запрос. Он не отвечает за порядок выполнения запросов. Он деле
гирует эту ответственность интерфейсу ScheduleOrder ing.
ScheduleOrdering. Объекты Request реализуют интерфейс, который выполняет эту роль. Выступающие в этой роли интерфейсы предназначены для решения задач двух типов.
1 . Ссылаясь на интерфейс Schedu leOrder ing, классы Proces sor не зависят от класса Reques t .
2. Вызывая методы, определяемые интерфейсом S chedul eOrderi ng, классы Schedu ler способны делегировать принятие решения о том, какой объект Request будет обработан следующим. Тем самым достигается независи мость- от типов решаемых задач классами Schedu ler. На рис. 9.20 такой ме тод
Взаимодействие между объектом Processor и объектом Scheduler происхо дит в два этапа (рис. 9.21). Оно начинается с обращения к методу do It объекта Processor. Метод doI t вызывает метод enter объекта Schedu ler, связанного
с объектом Processor . Если в данный момент никакой другой поток не вы
полняет остальную часть метода doI t , то метод enter выполняется немедлен но. После выполнения метода enter объект Scheduler знает, что управляе мый им ресурс занят. Когда ресурс занят, не будет выполнен ни один вызов метода enter объекта Scheduler до тех пор, пока ресурс не освободится и объ ект Scheduler не придет к решению, что подошла очередь выполнить очеред
ной запрос.
--+
1: dolt(r)
:Processor
1.2:done() 1
Этот метод не выполнится до тех
пор, пока Scheduler не
разрешит
:Scheduler
выполнение.
11.1.: scheduleBefOre(:ReQuest),...--''--_-,
r:Reguest
Рнс. 9.21.
Взаимодействие для Scheduler
478
• Глава 9. Шаблоны проектирования дпя конкурирующих операций
Объект Schedu ler считает управляемый им ресурс занятым до тех пор, пока Не
будет вызван метод done объекта Scheduler. Когда один поток обращается
к методу done объекта Scheduler и какие-либо другие потоки ожидают выпол
нения метода enter объекта Scheduler, тогда один из них будет выбран дЛя
выполнения.
Если вызов метода enter объекта Scheduler должен ожидать своего выполне_ ния и имеются другие вызовы, ожидающие выполнения метода enter, то объ ект Scheduler должен решить, какой вызов будет выполнен на этот раз. При нятие решения он делегирует объектам Request, которые были переданы этим методам в качестве параметров с целью определения, какой вызов будет выпол нен следующим. Объект Scheduler делает это через вызов методов, объявлен
HblXных интерфейсом ScheduleOrdering специально для этой цели и реализован
объектом Request .
РЕАЛИЗАЦИЯ
в некоторых случаях использования шаблона Scheduler класс Scheduler реа лизует такую политику планирования, которая не требует от него консультаций
собъектами Request с целью определения порядка выполнения обращений
кего методу enter. Примером такой политики может служить поведение, при котором разрешенный порядок выполнения обращений к методам enter соот ветствует порядку их вызова. В таких случаях нет необходимости передавать объекты Request методу enter ИЛИ создавать интерфейс ScheduleOrdering.
Другим примером этой политики может служить поведение, при котором поря док планирования запросов не имеет значения, но ставится условие, опреде ляюшее интервал между концом одного задания и началом другого, например, равный или превышающий пять минут.
@) Политика планирования инкапсулирована в своем собственном классе
иявляется многократно используемой.
®Использование шаблона Scheduler может служить причиной значительн дополнительных издержек, помимо расходов, необходимых для простоГО вызова синхронизированного метода.
ПРИМЕР КОДА
Приведем код, реализующий проект планирования вывода на печать, рае:
смотренный в разделе .Контекст». В первом листинге - реализация клас<i3
Printer, управляющего распечаткой записей журнала контрольного пункfI1