본문 바로가기

Uncategorized

C# Thread Message (쓰레드 메시지) 처리

 

간만에 C# 을 쓰니까 OCX 같은 부분은 참 편한데,

프로그래밍 관련해서는 여러 가지에서 막히게 되네요.

 

C# 으로 쓰레드 메시지 처리 부분을 확인하려고 검색을 좀 했더니,

쓰레드 프로그래밍을 처음 접하는 분들이 꽤 있는지

엉뚱한 말들이 많이 써져 있더라구요.

 

뭐 제가 찾은 해법도 완전히 맞는 방법일지는 확신할 수 없지만,

일단 큰 컨셉을 적으려고 글을 남깁니다.

 

그림 1.

 

우선, C# 에서 처음 만드는 Form 형식의 MainForm 은 윈도우 프로그램이기 때문에,

기본적으로 한 개의 UI Message Loop 를 가집니다.

 

그런데, 여기에서 모든 일을 처리하려고 하면,

UI Message Loop 가 원활하게 돌지 않기 때문에

다이얼로그가 먹통이 되는 것처럼 자꾸 느껴지게 됩니다.

 

그래서 시간이 걸리는 작업들은 Thread 1 처럼 쓰레드를 만들어서 동작을 시키고

그 결과를 메시지로 받는 것이 바람직합니다.

SendMessage 는 예시로 든 것이지만, 실제로는 UI Message Loop 에 영향을 주지 않게 하려면

PostMessage 방식도 괜찮습니다.

 

다만, 규칙이 있는데,

Thread 1 처럼 쓰레드로 분리된 곳에서는 MainForm 의 UI 콘트롤(버튼이나 에디터 위젯 등등)을 직접 액세스하지 말아야 합니다.

MainForm 의 UI 콘트롤, 위젯등등은 MainForm 내부에서만 이루어져야 합니다.

이게 C# 의 코드 컨벤션 특징 때문에 코드상으로 봐서는 구분이 잘 안 갈 수도 있겠습니다만,

각 함수가 MainForm 에서 실행되는지,

새로 만든 Thread 에서 전용으로 사용하는 함수인지에 따라 구분하시면 됩니다.

 

"어? 근데 코딩이 되는 되는데? 쓰레드에서 MainForm 변수들이 액세스가 되긴 되는데?"

라고 궁금해하시는 분들이 있으실텐데,

그건 쓰레드의 기본 특징 때문입니다.

 

쓰레드는 프로세스나 프로그램처럼 별개로 동작하는 특성을 가지지만,

기본적으로 메모리를 공유하는 특성이 있어서, 다른 변수들이 보이기는 합니다.

사실 그런 장점으로 쓰는 것이기도 한데,

쓰레드의 구분이나 메시지 루프의 개념을 혼동하게 되면

큰 재앙을 불러오는 프로그래밍을 하게 될 겁니다.

 

그림 2.

그림 2 에서 WndProc(), btnthKH001_Click(), btnNotifyTest1_Click() 함수들은 MainForm 에 속하고,

RunThread001() 함수는 쓰레드에 속하는 함수입니다.

 

RunThread001(void) 함수가 어떻게 동작하는지 보면,

쉽게 이해할 수 있습니다.

 

th001 쓰레드의 내용은 RunThread001(void) 함수 하나로 정리가 되는데,

쓰레드가 처음 시작되면 WM_USER + 101 이라는 메시지를 MainForm 에 보냅니다.

그 다음부터는 bThreadShouldSendMessage 가 true 인지만을 확인하고,

bThreadShouldSendMessage == false 라면 0.1 초 동안 sleep 에 들어갑니다.

무한루프가 걸리죠.

 

하지만, 쓰레드가 무한루프에 걸리는 것이니까 

MainForm 의 동작은 아주 부드럽게 잘 됩니다.

bThreadShouldSendMessage = true 로 만드는 조건은 MainForm 에서 다른 버튼을 누르는 것인데요.

MainForm 에서 다른 버튼을 누르면, 최대 0.1초 후에는 쓰레드가 WM_USER+102 메시지를 보내오게 됩니다.

그러면 MainForm 에서 WM_USER + 102 를 수신할 때 해야 할 일을 수행하게 됩니다.

 

UI Message Loop + N 개의 쓰레드 사이에서 양방향 메시지 수신을 사용하기도 하구요.

이 글에서 제시한 코드처럼,

만들어진 쓰레드는 무한루프를 돌면서 MainForm 의 상태를 관찰하다가

특정 상황이 관찰되면(Flag 혹은 변수값의 변경 등) 정해진 동작을 수행하는데,

정해진 동작은 쓰레드에서 함수를 직접 실행하는 것이 보통이지만,

MainForm 의 UI, 컨트롤, 위젯 들을 건드려야 할 때에는

MainForm 에게 메시지를 보내서, MainForm 이 처리하도록 해야 합니다.

 

아, 추가로, 윈도우 메시지를 사용하기 위해서는,

using System.Runtime.InteropServices;

 

 

이런 부분도 빼먹으면 실행이 잘 안 되겠죠.