В конечном итоге операция загрузки y (3) увидит значение true, записанное операцией сохранения (2). Поскольку сохранение производится в режиме memory_order_release, а загрузка — в режиме memory_order_acquire, то сохранение синхронизируется-с загрузкой. Сохранение x (1) происходит-раньше сохранения y (2), потому что обе операции выполняются в одном потоке. Поскольку сохранение y синхронизируется-с загрузкой y, то сохранение x также происходит-раньше загрузки y, и, следовательно, происходит-раньше загрузки x (4). Таким образом, операция загрузки x true, и, значит, утверждение (5) y не повторялась в цикле while, то высказанное утверждение могло бы оказаться неверным; операция загрузки y могла бы прочитать false, и тогда не было бы никаких ограничений на значение, прочитанное из x. Для обеспечения синхронизации операции захвата и освобождения должны употребляться парами. Значение, сохраненное операций восстановления, должно быть видно операции захвата, иначе ни та, ни другая не возымеют эффекта. Если бы сохранение в предложении (2) или загрузка в предложении (3) выполнялись в ослабленной операции, то обращения к x не были бы упорядочены, и, значит, нельзя было бы гарантировать, что операция загрузки в предложении (4) прочитает значение true, поэтому утверждение assert могло бы сработать.
К упорядочению захват-освобождение можно применить метафору человека с блокнотом в боксе, если ее немного дополнить. Во-первых, допустим, что каждое сохранение является частью некоторого пакета обновлений, поэтому, обращаясь к человеку с просьбой записать число, вы заодно сообщается ему идентификатор пакета, например: «Запиши 99 как часть пакета 423». Если речь идет о последнем сохранении в пакете, то мы сообщаем об этом: «Запиши 147, отметив, что это последнее сохранение в пакете 423». Человек в боксе честно записывает эту информацию вместе с указанным вами значением. Так моделируется операция сохранения с освобождением. Когда вы в следующий раз попросите записать значение, помер пакета нужно будет увеличить: «Запиши 41 как часть пакета 424».
Теперь, когда вы просите сообщить значение, у вас есть выбор: узнать только значение (это аналог ослабленной загрузки) или значение и сведения о том, является ли оно последним в пакете (это аналог загрузки с захватом). Если информация о пакете запрашивается, по значение не последнее в пакете, то человек ответит: «Число равно 987, и это 'обычное' значение»; если же значение последнее, то ответ прозвучит так: «Число 987, последнее в пакете 956 от Анны». Тут-то и проявляется семантика захвата-освобождения: если, запрашивая значение, вы сообщите человеку номера всех пакетов, о которых знаете, то он найдёт в своем списке последнее значение из всех известных вам пакетов и назовёт либо его, либо какое-нибудь следующее за ним в списке.
Как эта метафора моделирует семантику захвата-освобождения? Взгляните на наш пример — и поймете. В самом начале поток а вызывает функцию write_x_then_y и говорит человеку в боксе x: «Запиши true, как часть пакета 1 от потока а». Затем поток а говорит человеку в боксе y: «Запиши true, как последнюю операцию записи в пакете 1 от потока а». Тем временем поток b выполняет функцию read_y_then_x. Он раз за разом просит человека в боксе y сообщить значение вместе с информацией о пакете, пока не услышит в ответ «true». Возможно, спросить придется много раз, но в конце концов человек обязательно ответит «true». Однако человек в боксе y говорит не просто «true», а еще добавляет: «Это последняя операция записи в пакете 1 от потока а».
Далее поток b просит человека в боксе x назвать значение, но на это раз говорит: «Сообщи мне значение и, кстати, я знаю о пакете 1 от потока а». Человек в боксе x ищет в своем списке последнее упоминание о пакете 1 от потока а. Он находит единственное значение true, которое стоит последним в списке, поэтому он
Вспомнив определение отношения