
- •Сегменты стека
- •Динамическая память (куча)
- •Объектно ориентированный подход к проектированию системных модулей на основе концепций интерфейсов и реализаций.
- •Родовые данные или данные класса, значение которых относится не к экземпляру, а к типу в целом.
- •Активные объекты
- •Активные объекты с множественными рабочими потоками
- •Посылка синхронных сообщений
- •Выборка сообщений
- •Пример полной и упрощенной выборки
- •Сообщения, определяемые приложением
- •Локальная память потока
- •Трансляторы, компиляторы и интерпретаторы.
- •Общая схема работы транслятора
- •Понятие прохода
- •Генерация кода
- •Многоадресный код с явно именуемым результатом
- •Алгоритм вычисления выражений в полной записи
- •Современные системы программирования
-
Родовые данные или данные класса, значение которых относится не к экземпляру, а к типу в целом.
Еще одна дополнительная возможность, предоставляемая дроблением единой ссылки на данные, заключается в такой организации объекта, при которой его данные разнесены по нескольким блокам (возможно в нескольких библиотеках).
Достижение гибкости в варианте В привело к отказу от создания экземпляра клиентом, следовательно, сервер обязан предоставить возможность создания объекта по запросам клиента, возвращая ему заполненные правильными указателями интерфейсные таблицы.
В данном случае сервер обязан экспортировать функцию, создающую экземпляр объекта и возвращающую ссылку на его интерфейсную таблицу. Интерфейсная таблица содержит указатели на данные и может использовать разные функции (адреса) для обслуживания конкретного экземпляра.
Существует несколько подходов к разрушению однажды созданного экземпляра:
-
Сервер экспортирует функцию разрушения, принимая какую – либо информацию, позволяющую идентифицировать все занятые им ресурсы;
-
Освободить клиентов от управления временем жизни объектов (системы со сборкой мусора). В этом случае сервер реализует централизованную процедуру управления ресурсами, как правило, не относящимися к отдельному классу объектов;
-
Помещение в таблицу методов объекта функции уничтожения. При этом клиент управляет временем жизни, но фактически реализует разрушение не сервер в целом, а реализация класса, по определению имеющая полную информацию об обрабатываемом объекте.
Активные объекты
При реализации модульных систем часто возникает задача инициировать запуск некоторого действия в другом потоке инструкции.
В первом варианте запрос на вызов идентичен самому вызову и инструкции функции f работают в том же потоке, в котором произошло обращение (отсюда следует, например, разделение стека).
Во втором варианте инструкции f работают в другом потоке по отношению к инструкциям, запросившим этот вызов. Здесь поток – инициатор не занят во время работы функции f (асинхронный режим). При необходимости поток может дождаться возврата из запроса, а в более сложных утилизировать время ожидания. Такой подход позволяет разнести между клиентской и серверной сторонами не только данные алгоритмы, но и потоки управления, чем достигается дополнительная гибкость, в том числе возможность масштабируемого вычисления ресурсов на многопроцессорных системах.
При совмещении концепции вызова процедуры в другом потоке с объектно ориентированным подходом, а также требуемой реентерабельности получают специальный класс объектов, которые принято называть активными. Их основное отличие состоит в том, что все действия по обслуживанию их составляющих совершаются в изолированном внутреннем потоке. Активный объект, как правило, имеет дополнительный внутренний интерфейс, транслирующий вызовы в сообщения (инструкции), инкапсулирующие контекст вызова.
После изоляции контекста вызова в виде пакета или сообщения неважно, в каком потоке этот контекст будет восстановлен. При этом при восстановлении в другом потоке получается развязка по управлению.
Со стороны клиентов, которых может быть много в разных потоках, активный объект выглядит, как реентерабельный, т.е. клиенту не требуется использовать собственные средства синхронизации параллельных обращений. Но поскольку объект имеет внутреннее состояние, то параллельные действия над ним должны быть по меньшей мере упорядочены с точки зрения причинно – следственной связи, следовательно, все обращения образуют порядок очередности. Было бы неудобно возлагать упаковку контекста вызовов на клиента, как представлено на схеме. Небольшое усложнение с использованием методов заглушек позволит устранить неполадки.
Перенесение пакетов контекста в методы заглушки практически не снижает эффективность, т.к. соответствующие потери будут в любом случае, однако, появляется гибкость изменения алгоритма переноса контекста. Объекты, устроенные по такой схеме, ближе к 00 парадигме, в то время как явное формирование пакетов запросов больше напоминает клиент – серверную систему. Активный объект должен предусматривать в интерфейсе специальный метод (получение асинхронного результата). Т.о. почти все методы расслаиваются на коры (вызов асинхронный возврат). Хотя интерфейс становится при этом более громоздким, программа получает больше гибкости, например, она может игнорировать полученный результат.
Особое внимание стоит уделять ведению очередей, которые могут переполняться (входная при рассогласовании скоростей генерации запросов и их исполнения во внутреннем потоке, выходная – при отсутствии запросов на получение асинхронных результатов).
Бороться с переполнением входной очереди можно путем отслеживания времени пребывания в ней пакетов. В таком случае проигнорированные результаты по истечению некоторого тайм-аута удаляются автоматически, а при возникновении запроса к выходной очереди по истечению тайм-аута будет приводить к ошибке возвращения результата и будет являться индикатором неправильно спроектированного клиента.
Для организации активных объектов желательно использовать низкоуправляемые очереди сообщений, предоставленные операционной средой. Например, в Win32 существует группа функций, предназначенных как для помещения сообщений в очередь потока, так и для извлечения из нее.
Так как не всякий поток будет работать с сообщением, то операционная среда может оптимизировать процедуру создания потока, создавая его без очереди сообщений. При первом обращении к функции API, которая не может быть выполнена без очереди сообщений, такая очередь создается.