Jump to content

Получение ресурсов — это инициализация

Получение ресурса — это инициализация ( RAII ) [1] это идиома программирования [2] используется в нескольких объектно-ориентированных статически типизированных языках программирования для описания определенного поведения языка. В RAII хранение ресурса является инвариантом класса и привязано к времени жизни объекта . Выделение (или получение) ресурсов выполняется во время создания объекта (в частности, инициализации) с помощью конструктора , тогда как освобождение (освобождение) ресурсов выполняется во время уничтожения объекта (в частности, завершения) с помощью деструктора . Другими словами, для успешной инициализации получение ресурса должно быть успешным. Таким образом, ресурс гарантированно будет удерживаться между завершением инициализации и началом финализации (хранение ресурсов является инвариантом класса) и будет удерживаться только тогда, когда объект жив. Таким образом, если нет утечек объектов, нет и утечек ресурсов .

RAII наиболее тесно связан с C++ , где он зародился, а также с Ada . [3] Налейте , [4] и Руст . [5] Этот метод был разработан для безопасного управления ресурсами на C++. [6] в течение 1984–89 годов, прежде всего Бьярном Страуструпом и Эндрю Кенигом , [7] а сам термин был придуман Страуструпом. [8]

Другие названия этой идиомы включают Constructor Acquires, Destructor Releases (CADRe). [9] и один конкретный стиль использования называется управлением ресурсами на основе объема (SBRM). [10] Этот последний термин относится к частному случаю автоматических переменных . RAII связывает ресурсы со временем жизни объекта, которое может не совпадать с входом и выходом из области действия. (Примечательно, что время жизни переменных, размещенных в свободном хранилище, не связано с какой-либо конкретной областью действия.) Однако использование RAII для автоматических переменных (SBRM) является наиболее распространенным вариантом использования.

пример С++11

[ редактировать ]

Следующий пример C++11 демонстрирует использование RAII для доступа к файлам и мьютекса блокировки :

#include <fstream>
#include <iostream>
#include <mutex>
#include <stdexcept>
#include <string>

void WriteToFile(const std::string& message) {
  // |mutex| is to protect access to |file| (which is shared across threads).
  static std::mutex mutex;

  // Lock |mutex| before accessing |file|.
  std::lock_guard<std::mutex> lock(mutex);

  // Try to open file.
  std::ofstream file("example.txt");
  if (!file.is_open()) {
    throw std::runtime_error("unable to open file");
  }

  // Write |message| to |file|.
  file << message << std::endl;

  // |file| will be closed first when leaving scope (regardless of exception)
  // |mutex| will be unlocked second (from |lock| destructor) when leaving scope
  // (regardless of exception).
}

Этот код защищен от исключений, поскольку C++ гарантирует, что все объекты с автоматическим сроком хранения (локальные переменные) уничтожаются в конце охватывающей области видимости в порядке, обратном их созданию. [11] Таким образом, деструкторы как объекта блокировки , так и файлового объекта гарантированно будут вызываться при возврате из функции, независимо от того, было выдано исключение или нет. [12]

Локальные переменные позволяют легко управлять несколькими ресурсами в рамках одной функции: они уничтожаются в порядке, обратном их созданию, а объект уничтожается только в том случае, если он полностью создан, то есть если из его конструктора не распространяется исключение. [13]

Использование RAII значительно упрощает управление ресурсами, уменьшает общий размер кода и помогает обеспечить корректность программы. Поэтому RAII рекомендуется отраслевыми стандартами. [14] и большая часть стандартной библиотеки C++ следует этой идиоме. [15]

Преимущества

[ редактировать ]

Преимущества RAII как метода управления ресурсами заключаются в том, что он обеспечивает инкапсуляцию, безопасность исключений (для ресурсов стека) и локальность (он позволяет записывать логику получения и освобождения рядом друг с другом).

Инкапсуляция обеспечивается, поскольку логика управления ресурсами определяется один раз в классе, а не на каждом сайте вызова. Безопасность исключений обеспечивается для ресурсов стека (ресурсов, которые высвобождаются в той же области, в которой они были получены) путем привязки ресурса к времени жизни переменной стека (локальной переменной, объявленной в заданной области): если исключение выдается , и имеется правильная обработка исключений, единственный код, который будет выполняться при выходе из текущей области, — это деструкторы объектов, объявленных в этой области. Наконец, локальность определения обеспечивается путем записи определений конструктора и деструктора рядом друг с другом в определении класса.

Поэтому управление ресурсами должно быть привязано к сроку службы подходящих объектов, чтобы обеспечить автоматическое распределение и восстановление. Ресурсы приобретаются во время инициализации, когда нет никакой возможности использовать их до того, как они станут доступны, и освобождаются при уничтожении тех же объектов, что гарантированно происходит даже в случае ошибок.

Сравнивая RAII с finally конструкции, используемой в Java, Страуструп писал: «В реалистичных системах существует гораздо больше приобретений ресурсов, чем видов ресурсов, поэтому метод «получение ресурсов — это инициализация» приводит к меньшему количеству кода, чем использование конструкции «finally». [1]

Типичное использование

[ редактировать ]

Конструкция RAII часто используется для управления блокировками мьютексов в многопоточных приложениях. При таком использовании объект снимает блокировку при уничтожении. Без RAII в этом сценарии вероятность взаимоблокировки была бы высокой, а логика блокировки мьютекса была бы далека от логики его разблокировки. В случае RAII код, блокирующий мьютекс, по существу включает в себя логику, согласно которой блокировка будет снята, когда выполнение выйдет за пределы объекта RAII.

Другой типичный пример — взаимодействие с файлами: у нас может быть объект, представляющий файл, открытый для записи, при этом файл открывается в конструкторе и закрывается, когда выполнение выходит за пределы объекта. В обоих случаях RAII гарантирует только то, что рассматриваемый ресурс будет освобожден надлежащим образом; по-прежнему необходимо соблюдать осторожность для обеспечения безопасности исключений. Если код, изменяющий структуру данных или файл, не защищен от исключений, мьютекс может быть разблокирован или файл закрыт с повреждением структуры данных или файла.

Владение динамически выделяемыми объектами (память, выделенная с помощью new в C++) также можно управлять с помощью RAII, так что объект освобождается при уничтожении объекта RAII (на основе стека). Для этой цели стандартная библиотека C++11 определяет интеллектуальных указателей . классы std::unique_ptr для объектов, находящихся в единоличной собственности, и std::shared_ptr для объектов долевой собственности. Подобные классы также доступны через std::auto_ptr в С++98 и boost::shared_ptr в библиотеках Boost .

Также сообщения можно отправлять на сетевые ресурсы с помощью RAII. В этом случае объект RAII отправит сообщение в сокет в конце конструктора, когда его инициализация будет завершена. Он также отправит сообщение в начале деструктора, когда объект вот-вот будет уничтожен. Такая конструкция может использоваться в клиентском объекте для установления соединения с сервером, работающим в другом процессе.

Расширения «очистки» компилятора

[ редактировать ]

И Clang , и коллекция компиляторов GNU реализуют нестандартное расширение языка C для поддержки RAII: атрибут переменной «cleanup». [16] Следующее аннотирует переменную заданной функцией деструктора, которую она будет вызывать, когда переменная выйдет за пределы области видимости:

void example_usage() {
  __attribute__((cleanup(fclosep))) FILE *logfile = fopen("logfile.txt", "w+");
  fputs("hello logfile!", logfile);
}

В этом примере компилятор обеспечивает вызов функции fclosep для файла журнала до возврата example_usage .

Ограничения

[ редактировать ]

RAII работает только для ресурсов, приобретенных и освобожденных (прямо или косвенно) объектами, выделенными в стеке. где существует четко определенное время жизни статического объекта. Объекты, выделяемые в куче , которые сами приобретают и освобождают ресурсы, распространены во многих языках, включая C++. RAII зависит от объектов на основе кучи, которые должны быть неявно или явно удалены по всем возможным путям выполнения, чтобы вызвать деструктор, освобождающий ресурсы (или его эквивалент). [17] : 8:27  Этого можно достичь, используя интеллектуальные указатели для управления всеми объектами кучи и слабые указатели для объектов с циклическими ссылками.

В C++ раскручивание стека гарантированно произойдет только в том случае, если где-то перехватывается исключение. Это связано с тем, что «если в программе не найден соответствующий обработчик, вызывается функция завершения(); независимо от того, будет ли стек разматываться перед этим вызовом метода завершения(), определяется реализацией (15.5.1)». (Стандарт C++03, §15.3/9). [18] Такое поведение обычно приемлемо, поскольку операционная система освобождает оставшиеся ресурсы, такие как память, файлы, сокеты и т. д., при завершении программы. [ нужна ссылка ]

На конференции Gamelab 2018 года Джонатан Блоу объяснил, как использование RAII может вызвать фрагментацию памяти , что, в свою очередь, может привести к промахам в кэше в 100 или более раз и снижению производительности . [19]

Подсчет ссылок

[ редактировать ]

Perl , Python реализации CPython ), [20] и PHP [21] управлять временем жизни объекта посредством подсчета ссылок , что дает возможность использовать RAII. Объекты, на которые больше нет ссылок, немедленно уничтожаются или финализируются и освобождаются, поэтому деструктор или финализатор может в это время освободить ресурс. Однако в таких языках это не всегда идиоматично, и особенно не рекомендуется в Python (в пользу контекстных менеджеров и финализаторов из пакета слабых ссылок ). [ нужна ссылка ]

Однако время жизни объектов не обязательно привязано к какой-либо области действия, и объекты могут быть уничтожены недетерминировано или не уничтожены вообще. Это делает возможной случайную утечку ресурсов, которые должны были быть освобождены в конце некоторой области действия. Объекты, хранящиеся в статической переменной (особенно в глобальной переменной ), могут не быть финализированы при завершении программы, поэтому их ресурсы не освобождаются; Например, CPython не гарантирует финализацию таких объектов. Кроме того, объекты с циклическими ссылками не будут собираться простым счетчиком ссылок и будут жить неопределенно долго; даже если они будут собраны (путем более сложной сборки мусора), время и порядок уничтожения будут недетерминированными. В CPython есть детектор циклов, который обнаруживает циклы и финализирует объекты в цикле, хотя до CPython 3.4 циклы не собираются, если какой-либо объект в цикле имеет финализатор. [22]

  1. ^ Jump up to: а б Страуструп, Бьярне (30 сентября 2017 г.). «Почему C++ не предоставляет конструкцию «наконец»?» . Проверено 9 марта 2019 г.
  2. ^ Саттер, Херб ; Александреску, Андрей (2005). Стандарты кодирования C++ . Серия углубленного изучения C++. Аддисон-Уэсли. п. 24 . ISBN  978-0-321-11358-0 .
  3. ^ «Жемчужина № 70: Идиома блокировки прицела» . АдаКор . Проверено 21 мая 2021 г.
  4. ^ Проект Валадате. «Разрушение» . Учебное пособие по Vala версии 0.30 . Проверено 21 мая 2021 г.
  5. ^ «RAII — ржавчина на примере» . doc.rust-lang.org . Проверено 22 ноября 2020 г.
  6. ^ Страуструп 1994 , 16.5 Управление ресурсами, стр. 388–89.
  7. ^ Страуструп 1994 , 16.1 Обработка исключений: Введение, стр. 383–84.
  8. ^ Страуструп 1994 , с. 389. Я назвал эту технику «приобретение ресурсов — это инициализация».
  9. ^ Артур Чайковский (6 ноября 2012 г.). «Изменить официальный RAII на CADRe» . Стандарт ISO C++ — будущие предложения . Группы Google . Проверено 9 марта 2019 г.
  10. ^ Чоу, Аллен (01 октября 2014 г.). «Управление ресурсами на основе объема (RAII)» . Проверено 9 марта 2019 г.
  11. ^ Ричард Смит (21 марта 2017 г.). «Рабочий проект стандарта языка программирования C++» (PDF) . п. 151, раздел §9.6 . Проверено 7 сентября 2023 г.
  12. ^ «Как я могу справиться с неудачным деструктором?» . Стандартная основа C++ . Проверено 9 марта 2019 г.
  13. ^ Ричард Смит (21 марта 2017 г.). «Рабочий проект стандарта языка программирования C++» (PDF) . Проверено 9 марта 2019 г.
  14. ^ Страуструп, Бьярне ; Саттер, Херб (3 августа 2020 г.). «Основные рекомендации по C++» . Проверено 15 августа 2020 г.
  15. ^ «У меня слишком много блоков попыток. Что я могу с этим поделать?» . Стандартная основа C++ . Проверено 9 марта 2019 г.
  16. ^ «Указание атрибутов переменных» . Использование коллекции компиляторов GNU (GCC) . Проект ГНУ . Проверено 9 марта 2019 г.
  17. ^ Веймер, Уэстли; Некула, Джордж К. (2008). «Исключительные ситуации и надежность программ» (PDF) . Транзакции ACM в языках и системах программирования . Том. 30, нет. 2.
  18. ^ илдьярн (05 апреля 2011 г.). «RAII и размотка стека» . Переполнение стека . Проверено 9 марта 2019 г.
  19. ^ Gamelab2018 - Дизайнерские решения Джона Блоу по созданию Jai, нового языка для игровых программистов на YouTube
  20. ^ «Расширение Python с помощью C или C++: количество ссылок» . Расширение и встраивание интерпретатора Python . Фонд программного обеспечения Python . Проверено 9 марта 2019 г.
  21. ^ Хоббс (08 февраля 2011 г.). «Поддерживает ли PHP шаблон RAII? Как?» . Проверено 9 марта 2019 г.
  22. ^ «gc — интерфейс сборщика мусора» . Стандартная библиотека Python . Фонд программного обеспечения Python . Проверено 9 марта 2019 г.

Дальнейшее чтение

[ редактировать ]
[ редактировать ]
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: f1674551cd9fe91387ef4b7e7cf38426__1719018960
URL1:https://arc.ask3.ru/arc/aa/f1/26/f1674551cd9fe91387ef4b7e7cf38426.html
Заголовок, (Title) документа по адресу, URL1:
Resource acquisition is initialization - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)