2009. 9. 13. 20:30

멀티스레드와 MF


MSDN : CWnd Derived MFC Objects and Multi-threaded Applications

멀티스레드 애플리케이션을 MFC를이용하여 짠다면, MFC 객체를 스레드간에 넘겨서는
안된다. 이 일반적인 룰에서 한 스레드는 오직 자신이 생성한 MFC객체들만 접근해야한다.
이렇게 하지 않으면 assertion이나 기대치 못한 행동등의 런타임 문제를 발생시킨다.

Win32 프로세스 주소영역 안에서 동작하는 모든 스레드는 모든 전역,static 자료를
접근할 수 있다. 스레드는 TLS(Thread-Local-Storage)로 스레드 자신만의 자료를 쓸수있다.

윈도우는 스레드들에 의해 관리되므로 멀티스레드 환경하에서, MFC는 Thread Local Storage
안에 임시적적이거나 영구적인(temporary and permanent) 윈도우 핸들 맵을 관리한다.
물론 다른 GDI 객체나 디바이스 컨텍스트를 위한 핸들 맵 역시 그렇게 관리한다.
TLS에 관리되는 윈도우 핸들 맵은 여러 스레드들 사이에서 동시에 접근되어선 안된다.

CHandleMap::LookupPermanent()와 CHandleMap::LookupTemporary() 함수는 이런 사실들의
직접적인 예이다. 주어진 윈도우 핸들에 대해 이런 함수들은 MFC CWnd를 상속받은 객체와
연관있는 현재 스레드의 permenent하거나 temporary한 윈도우 핸들맵을 체크한다.
만일 그런 함수로 다른 스레드에 의해 생성된 윈도우 객체에 대한 MFC 객체를 찾으려고 한다면
함수 호출은 실패가 될것이다.

CHandleMap::LookupPermanent()와 CHandleMap::LookupTemporary()를 호출하는 함수들이
몇개 있다. CWnd::AssertValid()( ASSERT_VALID라는 매크로도 역시) 그런 함수들중 하나다.
이 함수는 객체의 유효성을 체크하기 위해 쓰인다. 만일 AssertValid()가 MFC 객체의 m_hWnd
맴버를 핸들맵에서 찾지 못하거나, 잘못된 엔트리에서 찾게되면 이 함수는 assertion을 일으킨다.
VC++ 2.1에서는 이런 assertion은 Wincore.cpp파일의 797과 788번재줄이며
VC++ 2.2에서는 Wincore.cpp파일의 804와 805번째줄,
VC++ 4.0에서는 Wincore.cpp파일의 871과 872번째 줄에 assertion이 생기게 된다

ASSERT_VALID 매크로는 MFC 소스코드 전반에 걸쳐 많이 존재한다. 따라서 만일 특정스레드에서
다른 스레드에 속하는 MFC 윈도우 객체에 ASSERT_VALID를 호출한다면, assertion이 발생한다.
만일 assertion이 발생하지 않았더라도, 다른 스레드에 생성된 윈도우를 조작하는것은 허락
된 것이 아니므로 비정상적으로 동작할 것이다.

이런 경우 MFC 객체를 이용하지 말고 윈도우 핸들로 작업해야 한다.
윈도우 핸들을 스레드간에 전달하는 것은 안전하다. 만일 스레드 A에서 스레드 B로
윈도우 핸들을 전달했다면, 스레드 B는 이 윈도우 핸들로 Send 나 Post Message 를 할 수 있다. 이 메시지가 처리될 때, 스레드 A의 컨텍스트로 바뀌게 되고, Cwnd::AssertVaild() 은 스레드 A의 윈도우 핸들맵을 체크하고 성공을 리턴할 것이다.

이런 시나리오에서 스레드 B는 역시 CWnd::Fromhandle() 함수를 통해서 스레드 B의 임시적인(temporary) 핸들맵에 임시적인 CWnd 객체를 생성할 수 있다. 그러나 이 객체는 스레드 A의 핸들맵에 존재하는 MFC 객체와 동기화 하는 방법이 없으므로 사용상 제약을 받을 것이다.

출처 : 인터넷 검색하면 나오는데 원본 출처는 불분명