안녕하세요. 까막입니다.
오랜만에 글을 남기네요.

C++ 에서 싱글톤 패턴을 적용하여 인스턴스를 생성할 때, 인스턴스가 리턴되지 않고
무한 대기(내부적으로는 아래 설명에 따라 무한 루프)에 빠지는 문제에 대한 내용입니다.
이 문제는 Visual Studio 2015 (c++ 11 포함 이상)부터 상위 버전에서 발생할 수 있는 문제라고 되어 있네요.

 

싱글톤에 대한 간략한 설명 
https://boycoding.tistory.com/109

 

C++ 디자인 패턴 05. 싱글턴 패턴, Singleton Pattern

싱글턴 패턴, Singleton Pattern 오직 한 개의 클래스 인스턴스만을 갖도록 보장하고, 이에 대한 전역적인 접근점을 제공합니다. (GoF의 디자인 패턴 181쪽) GoF의 싱글턴 패턴은 의도와는 달리 득보다는

boycoding.tistory.com

 

MSDN
https://docs.microsoft.com/en-us/cpp/build/reference/zc-threadsafeinit-thread-safe-local-static-initialization?view=msvc-170 

 

/Zc:threadSafeInit (Thread-safe Local Static Initialization)

Learn more about: /Zc:threadSafeInit (Thread-safe Local Static Initialization)

docs.microsoft.com

 

비슷한 현상
https://forum.juce.com/t/calling-my-singleton-causes-eternal-loop-in-init-thread-header/37340

 

Calling my singleton causes eternal loop in _Init_thread_header

Hi there, I’m experiencing issues with one of my singletons, which I assume is related to threads somehow (pardon my modest knowledge of C++ and multithreading). This is what my function looks like: SettingsManager& SettingsManager::getInstance() { stati

forum.juce.com

 

겪은 현상 설명
최근 Visual Studio 2013 버전에서 Visual Studio 2019로 마이그레이션을 진행하면서 겪은 내용을 남겨 놓고자 글을 씁니다.
싱글톤을 리턴할 때 MSDN에서는 스레드 세이프하도록 가드를 넣었다고 설명하고 있습니다.
(아래 그림이 첫번째 인스턴스를 생성할 때, 가드를 치는 c runtime 내부 소스입니다.)

/Zc:threadSafeInit(스레드로부터 안전한 로컬 정적 초기화) - MSDN 설명중 일부 발췌
/ Zc:threadSafeInit 컴파일러 옵션은 컴파일러에게 스레드로부터 안전한 방식으로 정적 로컬(함수 범위) 변수를 초기화하도록 지시하므로 수동 동기화가 필요하지 않습니다. 초기화만 스레드로부터 안전합니다. 다중 스레드에 의한 정적 지역 변수의 사용 및 수정은 여전히 수동으로 동기화되어야 합니다. 이 옵션은 Visual Studio 2015부터 사용할 수 있습니다. 기본적으로 Visual Studio는 이 옵션을 활성화합니다.

즉, 위의 내용은 싱글톤을 패턴을 포함한 정적 로컬 변수의 초기화시 안정성을 보장하는 옵션에 대한 설명이며
Visual Studio 2015 부터는 기본 옵션으로 포함되어 있습니다.

멀티 스레드 환경에서도 정적 변수 초기화시 별다른 lock 없이 사용할 수 있도록 보장하는 내용이지요.
그게 아니라면 별도로 lock 을 걸어서 안정성을 보장해 주어야 합니다.

최근 테스트된 환경에서 고속으로 돌아가는 개별 스레드에서 싱글톤 객체를 생성할 때, 초기화 코드를 호출하고
해당 코드가 리턴되기 전에 다른 스레드에서 또 객체 생성을 호출하면 위 그림의 while 루프에 빠져 무한 루프를
도는 현상이 확인 되었습니다.

통상적으로 코드 자체에는 문제 없고, 로직이나 이런것에 전혀 문제가 없는 상황에서 발생하는데요
이번에 확인된 부분은 콘솔 터미널 에서는 문제없이 돌아가는 실행파일이, 리모트 터미널 환경에서 hang 걸리는 
극악한 환경에서 발생해서 원인을 찾는데 상당히 애를 먹었네요.

아마도 리모트 환경에서는 콘솔에 비하여 바이너리 로딩이나 초기화 등에서 조금더 지연이 발생 하나 봅니다.

해법은 컴파일러 옵션에서 /Zc:threadSafeInit- 옵션을 넣어서 간단하게 해결할 수 있습니다.
즉, 기본옵션인 /Zc:threadSafeInit 를 강제로 해제하는 내용입니다.

이런 경우 해당 객체의 스레드 안정성은 직접 보장해 주시거나, 기존 동작에 문제가 없었다면 옵션만 넣고 빌드하시면 됩니다.

해당 자료를 검색하고, 해결하는 과정에서 작업이 끝나고 보니 아래 자료에 도움될만한 내용이 있었네요.
https://www.slideshare.net/utilforever/c-korea-3rd-seminar-c-visual-studio

 

[C++ Korea 3rd Seminar] 새 C++은 새 Visual Studio에, 좌충우돌 마이그레이션 이야기

C++11을 시작으로 모던 C++이 도입된 지도 어느새 6년이라는 시간이 흘렀습니다. 올해는 C++17 표준이 도입될 예정입니다. 그만큼 많이 개선되고 새로운 기능들이 많이 도입되었기에 실무에서 사용

www.slideshare.net

 

+ Recent posts