Ожидание по необходимости
Предположим, что после ожидания необходимых сепаратных аргументов началось выполнение некоторого сепаратного вызова (например buffer.remove). Мы уже видели, что это не блокирует клиента, который может спокойно продолжать свои вычисления. Но, конечно, клиенту может потребоваться синхронизация с поставщиком, и для продолжения работы ему нужно дождаться завершения вызова.
Может показаться, что для этого нужен специальный механизм (он, действительно, был предложен в некоторых языках параллельного ОО-программирования, например в Hybrid) для воссоединения вычисления родителя с его расточительным вызовом. Но вместо этого можно использовать предложенную Денисом Каромелем (см. раздел "Библиографические заметки" этой лекции) идею ожидания по необходимости. Она состоит в том, чтобы ждать столько, сколько действительно необходимо.
Когда клиенту нужно точно знать, что вызов a.r (...) для сепаратной сущности a, присоединенной к сепаратному объекту O1, завершился? В тот момент, когда нужен доступ к некоторому свойству O1, требуется, чтобы объект был доступен, а все предыдущие его вызовы были завершены. До этого можно делать что-либо с другими объектами, даже запускать новый вызов процедуры a.r (...) на том же сепаратном объекте, поскольку, как мы видели, при разумной реализации можно просто ставить такие вызовы в очередь так, что они будут выполняться в порядке поступления.
Напомним, что компоненты подразделяются на команды (процедуры), выполняющие преобразование целевого объекта, и на запросы (функции и атрибуты), возвращающие информацию о нем. Завершения вызовов команд ждать не нужно, а завершения запросов - вполне возможно.
Рассмотрим, например, сепаратный стек s и последовательные вызовы:
s.put (x1); ...Другие инструкции...; s.put (x2); ... Другие инструкции ...; value := s.item(которые в соответствии с правилом сепаратного вызова должны входить в некоторую подпрограмму с формальным аргументом s). Если предположить, что ни одна из "Других инструкций" не использует s, то единственной инструкцией, требующей ожидания, является последняя; ей необходима информация о стеке - его верхнее значение (которое в данном случае должно равняться x2).
Эти наблюдения приводят к главному в понимании ожидания по необходимости: после запуска сепаратного вызова клиент должен ожидать его завершения, только если это вызов запроса. Более точная формулировка правила будет приведена ниже после рассмотрения практического примера.
Ожидание по необходимости (также называемое "ленивым ожиданием" и похожее на механизмы "вызова по необходимости" и "ленивого вычисления", знакомые лисповцам и студентам, изучающим теоретическую информатику) позволяет запускать произвольные параллельные вычисления, устраняя ненужные ожидания и гарантируя ожидание, когда это действительно требуется.