c++에서 프로그램의 종료과 객체(instance)의 relase 타임에 관해 질문드립니다.
글쓴이: vudghkzm / 작성시간: 목, 2007/04/26 - 11:25오전
singleton 패턴으로 클래스 A를 만들고자 합니다.
아래의 2가지 방법으로 singleton 클래스 A 코드를 생각해보았습니다.
class A { static A* a; static A* getInstance() { if (a == NULL) a = new A(); return a; } };
class A { static A& getInstance() { static A a; return a; } };
첫번째 방법의 경우, 프로그램이 종료될 때 의도적으로 A의 인스턴스를 delete 하지 않는다고 하면... 첫번째 방법으로 생성한 A 인스턴스와 두번째 방법으로 생성한 A인스턴스는 정확히 언제 자원이 release되는 것인지 궁금합니다.
일반적으로 프로그램이 종료될때 release 된다라고 알고 있는데요...
만약 프로그램이 멀티 쓰레드 환경이고, A의 인스턴스에 동시에 접근하는 쓰레드가 여러개 있다고 할때, 프로그램이 정상 종료 혹은 비정상 종료등으로 종료될 때, A 인스턴스가 release되는 시점이 A에 접근하는 특정 쓰레드의 종료 시점보다 이전이면 문제가 될거 같습니다만... 이와 같은 경우에 실제로 어떻게 되는지 궁금합니다.
Forums:
signlenton pattern은
multithreading 환경에서는 신중해야 합니다.
http://www.bombaydigital.com/arenared/2005/10/25/1
http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
를 참조 하시구요.
저 같은 경우 getInstance 와 CreateInstance, DestoryInstance 이렇게 3개의 static 멤버를 만듭니다.
그래서 main 같이 main thread에서 만 실행되는 함수에서 CreateInstance 로 instance를 만들고 DestoryInstance 로 release 합니다.
이러면 getInstance에 lock을 걸 필요가 없어서 무리 없이 singlenton을 사용할수 있습니다.
無心
Singleton 사용 시 주의 사항
single-thread 환경이라면 정적 지역 변수 (getInstanc() 함수내에서의 static )를 사용하는 것이 가장 좋은 방법일 겁니다. 함수의 static은 함수가 호출되지 않으면 객체가 만들어지지 않고, 처음으로 호출될 때 개체가 생성됩니다. 게다가, application의 종료 시점에 파괴되는 것이 보장되구요... (More Effective C++ 항목 26 참고)
그렇지만 multi-threading 환경에서라면 thread들 사이의 경쟁 조건에 의해 문제가 발생할 소지가 있습니다. 이를 해결하기 위해서는 Double-Checked Locking 기법을 사용해서, locking 관련된 부하도 없으면서 thread-safe 한 singleton을 구현할 수 있습니다.
Double-Checked Locking을 사용한 경우 해당 singleton 개체의 파괴를 언제 하느냐와 관련된 문제는 남게 됩니다. sandro 님께서 말씀하신 대로, CreateInstance, DestoryInstance 를 만들어서 사용하는 것도 한가지 방법일 테고요, private smart pointer를 사용하는 것도 좋은 방법인 것 같습니다. 다음 url을 참고하세요.
http://www.codeguru.com/cpp/cpp/cpp_mfc/singletons/article.php/c755
위의 내용들과는 별도로, 근본적으로 다중의 상호작용 하는 singleton 들을 사용할 때에는 그 singleton들 사이의 생명 주기를 제어하는 데 있어서 "참조 무효화 현상"이라는 어려운 문제가 발생할 수 있습니다.
예를 들어 키보드, 디스플레이 장치, 로그 시스템의 세개의 singleton 객체들을 사용하는데, 키보드나 디스플레이 객체의 생성 및 파괴 과정에서 발생한 에러는 로그 객체를 통해 보고되어야 하고, 키보드나 디스플레이 객체의 생성에 실패한다면, 프로그램은 종료되어야 하는 경우를 생각해 볼 수 있겠습니다. 키보드 객체를 생성하는데 성공했지만, 디스플레이 객체를 생성에는 실패한 경우, 로그 객체를 만들어서 에러를 보고한 다음, 프로그램이 종료되어야 합니다. 문제는 종료 시점에서, 로그 객체가 키보드 객체보다 먼저 파괴될 것인데요, 키보드 객체가 파괴될 때 뭔가 다른 에러가 생겨 이를 로그 객체에 저장하려고 로그의 Instance()를 호출하게 되면, 이미 파괴된 로그 객체의 쭉정이(?)가 반환됨으로 인해 정의되지 않은 동작이 발생할 수 있습니다. (이에 대한 자세한 내용 및 대응책은 Modern C++ Design Chapter 6 을 참고하시기 바랍니다.)
따라서 singletone이 좋기는 하지만, 서로 상호작용하는 여러 개의 singleton을 설계할 경우에는 주의를 기울일 필요가 있습니다.
댓글 달기