리소스 획득이 초기화 중

Resource acquisition is initialization

리소스 획득 초기화(RAII)[1]는 특정 언어 동작을 설명하기 위해 여러 객체 지향의 정적인 형식의 프로그래밍 언어에서 사용되는 프로그래밍 숙어[2]. RAII에서 자원 보유는 클래스 불변성이며 개체 수명에 묶여 있다: 자원 할당(또는 획득)은 객체 생성(특정 초기화) 중에 생성자에 의해 수행되고, 자원 할당(또는 획득)은 소멸자에 의해 객체 파괴(특정적으로 최종화) 중에 수행된다. 초기화가 성공하려면 자원 획득이 성공해야 한다는 얘기다. 따라서 자원은 초기화가 완료되고 최종화가 시작될 때(자원을 보유하는 것은 클래스 불변함), 그리고 객체가 살아 있을 때에만 보유되도록 보장된다. 따라서 물체 누수가 없으면 자원 누수가 없다.

RAII는 그것이 시작된 인 C++와 가장 두드러지게 연관되어 있지만, D,[3] Ada,[4] Vala,[5] 그리고 Rust와도 연관되어 있다.[6] 이 기법은 1984-89년에 주로 Bjarne StrustrupAndrew Koenig에 의해 C++[7]에서 예외적으로 안전자원 관리를 위해 개발되었으며,[8] 용어 자체가 Strustrup에 의해 만들어졌다.[9] RAIII는 일반적으로 이니셜리즘으로 발음되며, 때로는 "R, A, double I"로 발음되기도 한다.[10]

이 관용어의 다른 이름으로는 Constructor Aquires, Destructor Release(CADRe)[11]가 있으며, 한 가지 특정 사용 방식을 SBRM(범위 기반 리소스 관리)이라고 한다.[12] 이 후반 용어는 자동 변수의 특별한 경우를 위한 것이다. RAII는 자원을 대상 수명에 연결하며, 이는 스코프의 진입과 퇴장과 일치하지 않을 수 있다. (특히 프리 스토어에 할당된 변수의 수명은 지정된 범위와 관련이 없다.) 그러나 자동변수(SBRM)에 RAII를 사용하는 것이 가장 일반적인 사용 사례다.

C++11 예제

다음의 C++11 예는 파일 액세스 및 뮤텍스 잠금을 위한 RAII의 사용을 보여준다.

#include <fstream> #include <아이오스트림> #include <<mutex> #include <스덱스셉트> #include <끈>  공허하게 하다 WriteToFile(경시하다 찌꺼기::끈을 매다& 메세지) {   // 뮤텍스는 (스레드 간에 공유되는) 파일에 대한 액세스를 보호하는 것이다.   정태의 찌꺼기::뮤텍스 뮤텍스;    // 파일에 액세스하기 전에 뮤텍스를 잠그십시오.   찌꺼기::lock_guard<찌꺼기::뮤텍스> 자물쇠를 채우다(뮤텍스);    // 파일을 열어 보십시오.   찌꺼기::중류의 파일("reason.txt");   만일 (!파일.is_open()) {     던지다 찌꺼기::runtime_time("파일 열기");   }    // 파일에 메시지 쓰기.   파일 << 메세지 << 찌꺼기::끝을 맺다;    // 범위를 벗어날 때 파일이 먼저 닫힘(예외 사항)   // 스코프를 떠날 때 잠금 소멸기에서 뮤텍스가 두 번째로 잠금 해제됨   // (예외가 없는 경우) } 

이 코드는 예외적으로 안전하다. 왜냐하면 C++는 모든 스택 객체가 스택 풀림(stack unwinding)이라고 알려진 엔클로저 범위 끝에서 파괴되는 것을 보증하기 때문이다. 따라서 록과 파일 객체의 소멸자는 예외를 던졌든 안 던졌든 기능에서 돌아올 때 호출될 것을 보장한다.[13]

국지적 변수는 단일 함수 내에서 여러 자원을 쉽게 관리할 수 있다. 즉, 구성의 역순으로 파괴되고, 완전하게 구성된 경우에만 물체가 파괴된다. 즉, 구성자로부터 예외가 전파되지 않는 경우.[14]

RAII를 사용하면 리소스 관리를 크게 간소화하고 전체 코드 크기를 줄이며 프로그램의 정확성을 보장할 수 있다. 따라서 RAII는 산업 표준 가이드라인에 의해 추천되며,[15] 대부분의 C++ 표준 라이브러리는 관용어를 따른다.[16]

혜택들

자원 관리 기법으로서의 RAII의 장점은 캡슐화, 예외 안전성(스택 자원에 대한 예외 안전성), 지역성(취득과 해제 논리를 서로 옆에 쓸 수 있도록 한다)을 제공한다는 점이다.

자원 관리 논리는 각 통화 사이트가 아닌 클래스에 한 번 정의되기 때문에 캡슐화가 제공된다. 스택 리소스(취득되는 것과 동일한 범위에서 방출되는 리소스)에 대해 예외 안전성을 제공한다. 스택 변수의 수명(특정 범위에 선언된 로컬 변수): 예외를 던지고 적절한 예외 처리가 있는 경우, 커러를 종료할 때 실행될 유일한 코드가 된다.범위란 해당 범위에 선언된 객체의 소멸자를 말한다. 마지막으로, 정의의 지역성은 클래스 정의에서 생성자와 소멸자 정의를 서로 옆에 적음으로써 제공된다.

따라서 자원 관리는 자동 할당과 매립을 위해 적합한 객체의 수명에 연계될 필요가 있다. 자원은 사용되기 전에 사용할 가능성이 없을 때 초기화 중에 획득하고, 같은 물체의 파괴와 함께 방출되는데, 오류가 발생하더라도 발생이 보장된다.

RAII와 를 비교하는 중 finally 스트루스트럽은 "현실적인 시스템에서는 자원의 종류보다 훨씬 더 많은 자원 획득이 있기 때문에 '자원 획득은 초기화' 기법이 '마지막' 구조의 사용보다 더 적은 코드로 이어진다"[1]고 썼다.

일반적인 용도

RAII 설계는 다중 스레드 애플리케이션에서 뮤텍스 잠금을 제어하는 데 종종 사용된다. 그 용도에서, 물체는 파괴되었을 때 자물쇠를 풀어준다. 이 시나리오에서 RAII가 없다면 교착 상태가 발생할 가능성이 높고 뮤텍스를 잠그는 논리는 잠금을 해제하는 논리와는 거리가 멀 것이다. RAII에서 뮤텍스를 잠그는 코드는 기본적으로 실행이 RAII 개체의 범위를 벗어날 때 잠금이 해제된다는 논리를 포함한다.

또 다른 일반적인 예는 파일과 상호 작용하는 것이다. 우리는 쓰기 위해 열려 있는 파일을 나타내는 오브젝트를 가질 수 있다. 여기서 파일은 생성자에서 열리고 실행이 오브젝트의 범위를 벗어날 때 닫힌다. 두 경우 모두 RAII는 해당 자원이 적절히 방출되도록 보장하며 예외 안전성을 유지하기 위해 주의를 기울여야 한다. 데이터 구조나 파일을 수정하는 코드가 예외적으로 안전하지 않으면 데이터 구조나 파일이 손상되어 뮤텍스가 잠금 해제되거나 파일이 닫힐 수 있다.

동적으로 할당된 개체의 소유권(메모리가 다음으로 할당됨) new C++)에서도 RAII(스택 기반) 물체가 파괴될 때 물체가 방출되도록 RAII로 제어할 수 있다. 이를 위해 C++11 표준 라이브러리는 스마트 포인터 클래스를 정의한다. std::unique_ptr 단일 소유 객체의 경우 std::shared_ptr 공유 소유권이 있는 개체의 경우 유사한 강의도 다음에서 이용할 수 있다. std::auto_ptr C++98에서 boost::shared_ptr Boost 라이브러리에서.

컴파일러 확장 "정리"

클랑GNU 컴파일러 컬렉션모두 RAII: "정리" 변수 속성을 지원하기 위해 C 언어에 대한 비표준 확장을 구현한다.[17] 다음 매크로에서는 변수가 범위를 벗어날 때 호출할 지정된 소멸기 함수로 변수에 주석을 달았다.

정태의 횡대로 공허하게 하다 클로즈프(파일 **fp) { 만일 (*fp) 클로즈드(*fp); } #no_fclose____(fclosep) 

이 매크로는 다음과 같이 사용할 수 있다.

공허하게 하다 sample_properties() {   _fclose_ 파일 *로그파일 = 터놓고 이야기("logfile.txt", "w+");   풋볼("안녕 로그파일!", 로그파일); } 

이 예에서 컴파일러는 example_usage가 반환되기 전에 logfile에서 호출될 fclosep 함수를 정렬한다.

제한 사항

RAII는 잘 정의된 정적 객체 수명이 있는 스택 할당 객체에 의해 획득 및 방출(직접 또는 간접)된 자원에만 작용한다. 스스로 자원을 획득하고 방출하는 힙할당 객체는 C++를 포함한 여러 언어로 일반적이다. RAII는 리소스 노출 소멸자(또는 동등한 것)를 트리거하기 위해 가능한 모든 실행 경로를 따라 암시적으로 또는 명시적으로 삭제되는 힙 기반 객체에 의존한다.[18]: 8:27 이는 스마트 포인터를 사용하여 모든 힙 객체를 관리함으로써 달성될 수 있으며, 주기적으로 참조되는 객체에 대한 취약점 포인터가 있다.

C++에서 스택 언런딩은 예외가 어디선가 포착된 경우에만 발생하도록 보장된다. 이는 "프로그램에서 일치하는 핸들러를 찾을 수 없는 경우 ()기능이 종료된다. ()이 종료되기 전에 스택이 해제되는지 여부 (15.5.1) (C++03 표준, §15.3/9)가 구현 정의되어 있기 때문이다.[19] (C++03 표준, §15.3/9) 운영체제가 프로그램 종료 시 메모리, 파일, 소켓 등과 같은 남아 있는 자원을 방출하기 때문에 이러한 동작은 보통 허용된다.[citation needed]

기준 카운팅

Perl, Python(CPython 구현 시),[20] PHP[21] 참조 카운팅으로 객체 수명을 관리하므로 RAIII 사용이 가능하다. 더 이상 참조되지 않는 객체는 즉시 파괴되거나 최종 확정되어 해제되므로 소멸자 또는 최종 결정자는 그 시간에 자원을 해제할 수 있다. 그러나, 그러한 언어에서는 항상 관용적인 것은 아니며, 특히 파이썬에서는 (약자 ref 패키지의 컨텍스트 매니저파이널라이저에게 유리하게) 낙담하고 있다.

단, 객체 수명은 반드시 어떤 범위에도 구속되지 않으며, 객체가 비결정적으로 파괴되거나 전혀 파괴되지 않을 수 있다. 이것은 어떤 범위의 끝에서 방출되었어야 했던 자원을 실수로 유출하는 것을 가능하게 한다. 정적 변수(아마도 글로벌 변수)에 저장된 개체는 프로그램이 종료될 때 최종 확정되지 않을 수 있으므로 해당 개체의 자원이 공개되지 않는다. 예를 들어 CPython은 이러한 개체의 최종화를 보장하지 않는다. 또한, 원형 참조가 있는 물체는 단순한 참조 카운터에 의해 수집되지 않고, 제한 없이 오래 살 것이다; 비록 수집되었다 하더라도, (더 정교한 쓰레기 수거에 의해), 파괴 시간과 파괴 질서는 결정적이지 않을 것이다. CPython에는 사이클을 감지하고 사이클에서 물체를 최종화하는 사이클 검출기가 있지만, CPython 3.4 이전에 사이클의 물체에 최종 결정기가 있으면 사이클이 수집되지 않는다.[22]

참조

  1. ^ a b Stroustrup, Bjarne (2017-09-30). "Why doesn't C++ provide a "finally" construct?". Retrieved 2019-03-09.
  2. ^ Sutter, Herb; Alexandrescu, Andrei (2005). C++ Coding Standards. C++ In-Depth Series. Addison-Wesley. p. 24. ISBN 978-0-321-11358-0.
  3. ^ "Scope guards". Dlang Tour. Retrieved 21 May 2021.
  4. ^ "Gem #70: The Scope Locks Idiom". AdaCore. Retrieved 21 May 2021.
  5. ^ The Valadate Project. "Destruction". The Vala Tutorial version 0.30. Retrieved 21 May 2021.
  6. ^ "RAII - Rust By Example". doc.rust-lang.org. Retrieved 2020-11-22.
  7. ^ 스트루스트럽 1994, 16.5 자원 관리, 페이지 388–89.
  8. ^ Strustrup 1994, 16.1 예외 처리: 소개, 페이지 383–84.
  9. ^ 스트루스트럽 1994, 페이지 389. 나는 이 기법을 "자원 획득은 초기화"라고 불렀다.
  10. ^ Michael Burr (2008-09-19). "How do you pronounce RAII?". Stack Overflow. Retrieved 2019-03-09.
  11. ^ Arthur Tchaikovsky (2012-11-06). "Change official RAII to CADRe". ISO C++ Standard - Future Proposals. Google Groups. Retrieved 2019-03-09.
  12. ^ Chou, Allen (2014-10-01). "Scope-Based Resource Management (RAII)". Retrieved 2019-03-09.
  13. ^ "How can I handle a destructor that fails?". Standard C++ Foundation. Retrieved 2019-03-09.
  14. ^ Richard Smith (2017-03-21). "Working Draft, Standard for ProgrammingLanguage C++" (PDF). Retrieved 2019-03-09.
  15. ^ Stroustrup, Bjarne; Sutter, Herb (2020-08-03). "C++ Core Guidelines". Retrieved 2020-08-15.
  16. ^ "I have too many try blocks; what can I do about it?". Standard C++ Foundation. Retrieved 2019-03-09.
  17. ^ "Specifying Attributes of Variables". Using the GNU Compiler Collection (GCC). GNU Project. Retrieved 2019-03-09.
  18. ^ Weimer, Westley; Necula, George C. (2008). "Exceptional Situations and Program Reliability" (PDF). ACM Transactions on Programming Languages and Systems. 30 (2).
  19. ^ ildjarn (2011-04-05). "RAII and Stack unwinding". Stack Overflow. Retrieved 2019-03-09.
  20. ^ "Extending Python with C or C++: Reference Counts". Extending and Embedding the Python Interpreter. Python Software Foundation. Retrieved 2019-03-09.
  21. ^ hobbs (2011-02-08). "Does PHP support the RAII pattern? How?". Retrieved 2019-03-09.
  22. ^ "gc — Garbage Collector interface". The Python Standard Library. Python Software Foundation. Retrieved 2019-03-09.

추가 읽기

외부 링크