opencv를 이용하여 MFC로 캠 2개를 돌리는 프로그램을 구현중이었습니다.
캡쳐 이미지를 계속해서 화면에 뿌리기 위해서 캠 수만큼의 스레드를 돌리도록 설계를 하였습니다.
간편하게 MFC에서 스레드를 돌리는 방법으로는, worker thread를 이용하는 것입니다.
worker thread는, AfxBeginThread API와 전역 함수(혹은 클래스 정적 함수)를 이용하는 것인데,
전역 함수를 쓰다보니 자연스레 전역 변수를 사용하게 되버리고,
java에 익숙한 MFC 초짜인 저로썬 왠지 맘에 들지 않더군요.-_-;;
그래서
변수들은 인스턴스가 들고 다녀야지!! 객체지향스럽게!!
란 생각으로 눈을 돌린 쪽은 user interface thread 입니다.
user interface thread는 CWinThread를 상속받아서 사용하는 것으로,
thread 내부에 변수나 사용자 정의 함수등을 추가할 수 있으며,
MESSAGE 처리를 지원한다는 장점이 있더군요!!
이를 사용하기 위해선 MFC 의 매크로 함수를 여기 저기 적어줘야합니다.
구글링을 해보면 사용법들이 쭉쭉 나오는데 대충만 정리해보자면,
1. class 선언부에(헤더파일)
DECLARE_DYNCREATE(클래스명)
2. class 구현부에(cpp파일)
IMPLEMENT_DYNCREATE(클래스명, CWinThread)
을 써주는것부터 시작되어
virtual int Run();
virtual BOOL InitInstance();
virtual int ExitInstance();
등을 가상함수로 오버라이딩 해야 한다더군요.
(InitInstance : 스레드 시작시 호출. 초기화 개념. return TRUE 를 해줘야 스레드가 시작된다.
ExitInstance : 스레드 종료시 호출. return의 의미 아직 찾아보지 않았다.
Run : 스레드 시작부. 필수는 아님. 이유는? CWinThread 에는 이미 Run이 구현되어 있기 때문)
스레드를 돌리는 방법은
CWinThread *변수 = AfxBeginThread(RUNTIME_CLASS(클래스명));
으로 실행하거나
클래스명 *변수 = new CamPlayer();
변수->CreateThread();
로 실행할 수 있습니다.
갠적으로, 아래것이 더 좋더군요.
이유인 즉슨, 포인터 타입이 CWinThread가 아닌, 내가 만든 클래스의 포인터이기 때문에
직접 추가한 함수나 변수를 손쉽게 호출 및 접근할 수 있기 때문이죠.
(둘의 차이가 어떻게 나는지 더 나는지는 찾아보지 않았습니다)
이제 여기서부터 대대적인 삽질이 시작됩니다.
기왕 CWinThread로 가는것, MESSAGE를 적극 활용해보기 위하여 아래와 같이 정의하였습니다.
// ..h
#define WM_INIT_CAM (WM_USER+1)
#define WM_START_CAM (WM_USER+2)
#define WM_END_CAM (WM_USER+3)
// ..cpp
BEGIN_MESSAGE_MAP(CamPlayer, CWinThread)
//{{AFX_MSG_MAP(CMyWinThread)
ON_THREAD_MESSAGE(WM_INIT_CAM, OnInitCam)
ON_THREAD_MESSAGE(WM_START_CAM, OnStartCam)
ON_THREAD_MESSAGE(WM_END_CAM, OnEndCam)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
// ..h 의 class 선언부
DECLARE_MESSAGE_MAP()
afx_msg void OnInitCam(WPARAM wParam, LPARAM lParam);
afx_msg void OnStartCam(WPARAM wParam, LPARAM lParam);
afx_msg void OnEndCam(WPARAM wParam, LPARAM lParam);
그리고 다열로그에서 열심히 메시지를 날렸죠.
player1->PostThreadMessage(WM_END_CAM, NULL, NULL);
그런데, thread 가 메시지를 못받더군요. 왜? 왜?
처음 써보는거니, 잘못썼나 싶어서 별 짓을 다 해봤습니다. 안됩니다.
계속해서 삽질했습니다. 계속 안됩니다.
결국 알아냈습니다;;
이유는, Run 을 오버라이딩했기 때문;;;
Run을 지우니 비로소 메시지를 받기 시작합니다.
바로 CWinThread의 Run() 함수가 메시지를 처리하고 있었던 것이지요.
오버라이딩한 Run 내부에 CWinThread::Run() 을 추가해주니 잘 됩니다.
한번 Run함수를 MSDN 에서 찾아보았습니다.(여기)
Run acquires and dispatches Windows messages until the application receives a WM_QUIT message. If the thread's message queue currently contains no messages, Run calls OnIdle to perform idle-time processing. Incoming messages go to the PreTranslateMessage member function for special processing and then to the Windows function TranslateMessage for standard keyboard translation. Finally, the DispatchMessageWindows function is called.
Run is rarely overridden, but you can override it to implement special behavior. This member function is used only in user-interface threads. |
대충 요악해보면, WM_QUIT 메시지를 받기 전까지 잘 돌고, 메시지 없으면 OnIdle 수행한다는 말인데 허걱!!
허걱.....
Run is rarely overridden
이런 낭패가;;;;;
많은 웹상의 설명이나 예제들이 run을 구현하라고 되있더군요.
(사실 run을 구현할라믄, 그냥 편하게 worker thread를 쓰면 되는 겁니다.)
심지어 영어로 저 문장을 써놓고 "Run은 거의 오버라이드 한다" 라고 오역되있는 것도 보았습니다 -_-;;
역시, 웹상의 자료를 무조건 신뢰하면 안된다는걸 뼈저리게 느꼈습니다.
<< 결론 >>
user interface thread를 이용하여 thread message 기반 스레드를 작성시에는 Run을 오버라이드 하면 안된다. (걍 전역 함수 만들어서 worker thread로 돌리는게 편하다 -_-;;)
MFC 초짜의 Thread 삽질기 끝.
'업 > 삽질기' 카테고리의 다른 글
| MFC의 CWinThread 이용시의 MESSAGE 처리 (0) | 2009/10/09 |
|---|



