Все схемы, с которыми мы сталкивались до сих пор, относились к типу, который мы называем "батч", и, как правило, имели поток "слева направо", при этом IP создавались с левой стороны и удалялись с правой. Иногда нам нужно использовать другую топологию, которая представляет собой петлевую сеть. Некоторые из последующих глав содержат примеры этой топологии, поэтому стоит потратить некоторое время на обсуждение этого вида сетей на общем уровне. На самом деле многие сети представляют собой смесь этих двух типов, но как только вы поймете основные принципы, они не будут представлять никаких проблем.
Вот очень простой пример сети петлевого типа:
Фрагмент 14.1
Первый вопрос, на который мы должны ответить об этом типе сети: как она запускается? Возможно, вы помните, что автоматически запускаются только те процессы, у которых нет входных соединений (IIP не учитываются). Если вы посмотрите на рисунок 14.1, то увидите, что нет процессов без входных соединений! B имеет входное соединение, исходящее от A, но A имеет входное соединение, исходящее от B! Хотя иногда у нас возникало искушение ослабить ограничение на то, какие процессы могут запускаться, проще всего просто добавить дополнительный процесс, который не имеет входных соединений, а затем использовать его для запуска A или B. Таким образом, картина теперь выглядит так:
Фрагмент 14.2
Где K — процесс-запускатель (kicker), который выдает один пакет, содержащий пустоту. K может быть подключен либо к A, либо к B, как того требует логика.
Сейчас мы запустили нашу петлевую сеть, неудивительно, что есть еще одна проблема: как она выключается? Проблема здесь заключается в определении закрытия процесса — процесс закрывается при следующей деактивации после того, как все его вышестоящие процессы закрыты. На приведенной выше диаграмме, поскольку A находится выше по течению от B, но B также выше по течению от A, мы получаем ситуацию уловки-22: A не может закрыться, потому что B не может закрыться до тех пор, пока A не закроется, и так далее. Решение состоит в том, чтобы предоставить специальный сервис, который заставляет процесс выглядеть для своих соседей так, как будто он закрылся. Затем один из задействованных процессов должен решить закрыться и использовать эту службу для уведомления других процессов. В batch-сетях выключение обычно инициировалось закрытием программ чтения (поскольку они закончили чтение своих файлов или столкнулись с проблемами). В петлевых сетях один из процессов — обычно взаимодействующий с пользователем — должен решить, что данные больше не поступят, поэтому он закрывается.
Служба, которая сообщает соседям процесса, что он закрылся, — это служба, которую мы вскользь упомянули ранее: «закрыть порт». FBP позволяет компоненту закрыть порт ввода или закрыть порт вывода. Функция этой службы состоит в том, чтобы закрывать порты до того, как они закроются сами (обычно это происходит автоматически во время закрытия процесса, но бывают случаи, подобные этому, когда мы просто не можем ждать так долго). Процесс, закрывающий выходной порт, оказывает такое же влияние на нижестоящие процессы, как если бы процесс завершился. Процесс, закрывающий входной порт, оказывает такое же влияние на вышестоящие процессы; также, если все его входные порты теперь закрыты, он автоматически завершается.
Таким образом, чтобы закрыть сеть, A или B на рис. 14.2 просто закрывают свой входной или выходной порт — неважно, какой именно. Предположим, B закрывает свой входной порт и завершает выполнение: теперь оно завершится (поскольку входные данные больше не могут поступать). На самом деле B является восходящим процессом A, поэтому A также сможет закрыться, что приведет к остановке всей сети (процесс «kicker» уже давно закрылся).
Теперь, мы знаем, как запускать и останавливать петлевые сети, но зачем нам их использовать? Обычно это связано с синхронизацией, о которой мы также поговорим в следующей главе. В обычной сети с направлением слева направо левая сторона сети будет обрабатывать последние IP, в то время как более ранние IP обрабатываются справа в сети. Эта асинхронность придает этому методу большую силу, но бывают ситуации, когда вам нужно согласовать некоторую обработку с конкретным внешним событием или убедиться, что две функции не могут перекрываться во времени. Одним из таких примеров является интерактивное приложение, поддерживающее одного пользователя. Здесь A в приведенном выше примере может быть компонентом интерактивного ввода-вывода, а B может быть компонентом для обработки ввода и создания соответствующего вывода, например:
Фрагмент 14.3
Тут INTER управляет экраном. INTER получает некоторые данные от PROC, отображает их на экране, ждет какого-либо действия со стороны конечного пользователя, а затем отправляет информацию обратно в PROC. Если программная инфраструктура позволяет это, ожидание ввода нужно только приостановить INTER, и другие процессы могут работать над их вводом, пока INTER приостановлен.
С другой стороны, если бы это был поток слева направо, а PROC предшествовал процесс ввода, а за ним процесс вывода (без «обратного потока»), ввод и вывод на один и тот же экран и с него больше не синхронизировались бы. Пришлось бы синхронизировать по крайней мере один компонент с темпом пользователя, чтобы он или она могли действовать в соответствии с данными, представленными на экране, прежде чем перейти к следующему экрану.
Поскольку IP, из которых строится экран, должны вписываться в очереди цикла и/или рабочее хранилище процессов, мы должны убедиться, что в этих очередях достаточно емкости. Один из способов убедиться, что нам не о чем беспокоиться, — это использовать древовидные структуры, описанные в предыдущей главе. Дерево IP может использоваться для представления данных экрана и может передаваться по циклу как единое целое. В качестве альтернативы данные экрана могут быть представлены в виде одного или нескольких подпотоков, и тогда нам просто нужно убедиться, что общая емкость очереди достаточно высока.
Как мы увидим в главе 19, где мы описываем интерактивные приложения IBM IMS, мы также придём к петле, но с другой целью. IMS — это онлайн-среда, управляемая очередями, и ее программы обработки сообщений (MPP) продолжают получать транзакции из очереди сообщений IMS до тех пор, пока сообщения не кончатся или пока не будут выполнены определенные условия, которые заставят MPP закрыться. Каждый раз, когда транзакция получается из очереди, IMS делает точку синхронизации, поэтому любая информация о местоположении из предыдущей транзакции теряется, базы данных обновляются и т. д. Поэтому в FBP мы строим MPP как циклы, из которых следующая транзакция не считывается. очередь сообщений, пока предыдущее не будет полностью обработано. Диаграмма будет такой же, как и предыдущая, за исключением того, что INTER заменен «получателем транзакций», как показано ниже:
Фрагмент 14.4
Если вы знакомы с IMS, вы также поймете, что каждый раз в этом цикле программа обычно имеет дело с разными пользователями, поэтому, в отличие от предыдущего примера, вы не можете использовать рабочее хранилище процессов для сохранения информации, относящейся к одному пользователю.
Очевидно, что в обоих вышеупомянутых случаях PROC является сокращением для группы процессов, которые совместно обрабатывают один экран или транзакцию. Эта группа процессов будет принимать данные с экрана и отправлять какой-либо сигнал, когда они закончат работу с ними. В обоих случаях на самом деле не имеет значения, перенаправляются ли выходные данные экрана обратно в INTER или GETXACT или выводятся другим процессом, пока ввод не принимается если пока вывод ещё не представлен пользователю.
Другое использование петлевых сетей - это «взрывные» приложения, классическим примером которых является взрыв "Bill of Materials", когда компоненты некоторой сложной сборки постепенно «разбиваются» на подкомпоненты, покуда не останутся те, которые не могут быть разбиты дальше. Если вы знаете, что самый большой возможный взрыв не заполнит хранилище, вы можете использовать цикл из двух или более процессов с очередями очень большой емкости, соединяющими их (использовать цикл только с одним процессом опасно, так как вы можете поймать дедлок). Конечно, IP для составных компонентов должны быть удалены из потока данных, когда к нему добавляются их подкомпоненты, или когда обнаруживается, что они не поддаются дальнейшему разбору (как гайки и винты), так что в конечном итоге циклический поток данных станет пустым.
Этот тип логики также может быть полезен при анализе других видов рекурсивных структур, например, списки списков или выражений в языке. Коллега, Чарльз Дуглас очень эффективно использовал это в приложении для обработки текста, где пользователю нужно было иметь возможность давать имена спискам баз данных и, в свою очередь, использовать эти имена в других списках. Он реализовал это очень похоже на взрыв Bill of Materials. Его приложение прошло через все списки, постепенно расширяя их, пока не дошло до фактического уровня базы данных. Итак, предположим, что у нас есть следующие списки:
A: B, C, D, E
B: D, E, G
D: E, F
Фрагмент 14.6
Затем, если вы подаете A, последовательные стадии взрыва будут следующими:
A
B, C, D, E
D, E, G, C, E, F, E
E, F, E, G, C, E, F, E
Фрагмент 14.7
Если цель состоит в том, чтобы выяснить, сколько у вас есть каждого атомарного объекта, итоги будут следующими:
1 C, 4 E, 2 F, 1 G
Фрагмент 14.8
Если вы просто хотите перечислить различные задействованные атомарные объекты, вы получите:
C, E, F, G
Фрагмент 14.9
В любом случае, самый простой метод — следовать за взрывом с помощью сортировки, а затем либо подсчитывать элементы, либо устранять дубликаты.



