C++/소켓통신

MFC에서 소켓을 이용한 파일전송기 만들기(3장-멀티스레딩)

gandus 2011. 5. 16. 17:11
출처 : 데브피아 - 최훈익 님의 강좌




여러분! 드디어 2장을 탈출해 3장을 올리게 되었습니다. 3장도 작성해놓고 다시 읽어
보니 역시나 허접하지만, 이번장을 마치게 되어 아마도 다음 장부터는 본론에 들어갈 
수 있을 것 같습니다. 우리 다같이 열심히 공부해 봅시다..

이런 이야기를 하면.. 아마도 자랑인거 같아서 좀 그렇지만, 저에게 팬레터(쪽지)를 
보내 주시는 분들이 계십니다. 감사합니다. 여러분들의 격려의 메시지들을 보면 저도 
힘이 납니다. 보다 알찬 강좌가 되도록 더욱 노력하겠습니다. 부족하다는 것을 잘 알
고 있지만, 그래도 무엇이든 열심히 노력한다는 것이 중요한 거 같습니다.

------------------------------------------------------
제3장 멀티스레딩 
------------------------------------------------------

여러분은 소켓 프로그래밍을 할 때 가장 필요한 지식은 무엇이라고 생각하는가? 
"당연히 소켓아니냐?" 하고 반문 하실지 모르겠다. 이 답이 틀렸다는 얘기는 아니다. 
하지만 내 생각은 다르다. 소켓을 알아야 소켓을 프로그래밍 할 수 있겠지만, 그보다
도 스레드에 대해 더 잘 알고 있어야 한다고 생각한다.
MFC에서 소켓을 사용하기는 쉽다. 일반적으로 소켓은 동작하는 동안 CPU타임을 독차
지 한다. 엄밀히 말해 시스템 전체를 홀딩시키지는 않지만(소켓을 사용하는 프로그램
이 죽은 듯이 보일 때에도 운영체제는 여전히 정상으로 돌아가고 있다), 자신의 프로
세스 내에서는 다른 어떤 작업에게도 CPU 타임을 양보하지 않는다. (헉... 도데체 이 
이야기들은 무슨 이야기 인고? 심히 난해하도다.... 스레드는 무엇이고 프로세스는 무
엇이란 말인가? ) 혹시 앞의 이야기가 무슨 이야기인지 통 모르겠다고 해도 걱정할 
것 없다. 이번 장에서 이것들을 알려 줄 것이다.

무슨 이야기 인고 하니, 나는 이장에서 스레드에 대해서 이야기 할 것인데, 그것은 어
디까지나 우리의 프로그램에서 소켓을 제대로 사용하기 위해서이지, 스레드를 위해서 
스레드를 이야기 하는 것이 아니다. MFC에서는 멀티스레딩을 위해서 두 가지의 방법
을 제시하고 있는데, 나는 이들 중에서 내가 좋아하는 방식만 이야기 할 것이다. 여
기 강좌란을 둘러보면, 멀티 스레딩에 대해 이야기 하고 있는 강좌들을 쉽게 찾을 수 
있다. 스레드에 대해서 더 알고자 한다면, 이 강좌들을 참고하자.

1. 프로세스와 스레드
자.. 이제 시작해보자..
먼저, 프로세스란 무엇인가?
글쎄... 잘 모르겠다(..헉.... ㅡ.ㅡ;) 
프로세스를 이야기 할려면 윈도우즈 운영체제 하에서 돌아가는 프로그램이 실행되는 
과정을 이야기 해야만 한다.
우리가 바탕화면에서 실행파일의 아이콘을 더블클릭하면 이 프로그램이 실행된다. 이 
과정의 내부를 한번 이야기 해보자. 
프로그램이 처음 실행되어야 한다는 것을 운영체제가 알아차리면, 운영체제는 메모리
상에 그 프로그램의 소스코드(C++로 되어있는 코드가 아니라 컴파일되서 기계어가 되
어있는 이진 데이터)를 복사해서 옮겨놓을 공간을 확보한다. 그리고 그 프로그램의 코
드를 복사한 뒤, 프로그램이 변수와 함수호출 등을 위해 사용할 메모리 공간(스택)을 
예약해 놓는다. 그 밖에도 커맨드 라인 포인터가 프로그램의 첫부분(엄밀히 말해 다
음 스텝에서 실행해야 할 라인)을 가리키도록 레지스터 메모리를 간단히 정리해 놓는
등의 우리가 도무지 알 수 없는 여러가지의 일들을 한다. 그리고 프로그램에게 실행해
도 좋다는 사인을 보내는데, 프로그램은 처음 자신의 윈도우를 만들고, 그 윈도우에 
대한 핸들을 윈도우로 부터 부여 받는다. 그리고는 자신의 코드를 한줄 한줄 실행해 
나간다. 이 과정에서 메모리상에 카피된 프로그램 코드의 복사본을 프로세스라고 할 
수 있다. 이렇게 이야기 하면 프로세스란 꼭 하드웨어상의 물리적인 개념인 것 같은
데... 이부분을 상당한 시간을 (담배피우면서)고민했지만, 어떻게 적절히 설명해야 할
지 모르겠다.
대충 위의 이야기가 이해됬다면 참으로 다행이다. 프로세스란 실행되기 위해 메모리
에 자리를 잡고 들어앉은 프로그램 자신을 말하는 것이라고 볼 수 있다. 우리가 코드 
상에서 클래스를 하나 정의했다고 해보자. 이 클래스는 당연히 자신의 선언들을 가진 
헤더파일과 정의들을 가진 소스파일이 있을 것이다. 그러나 이 파일들을 프로젝트에 
추가했다고 해서 이 클래스가 구동되는 것은 아니다. 이 클래스를 쓰기 위해서는 인스
턴스를 생성해야 한다. 오오.... 이 얼마나 적절한 예인가?? 클래스의 인스턴스를 생
성하면, 실행시간에는 이 클래스가 들어앉을 메모리가 할당되는 것이다. 우리가 int 
nInteger; 와 같이 했다면 이 정수형 변수를 담을 메모리 그릇을 만든 것이라는 것은 
누구나 다 알고 있을 것이다. 이와 같이 CMyClass myClass;하고 클래스의 인스턴스를 
선언하면 이 클래스를 위한 메모리를 할당한 것이다. 이렇게 자신을 위해 할당된 메모
리에 올라간 클래스는 이제 자신이 할 일을 할 수 있는 것이다.
응용프로그램도 마찬가지로 자신을 위해 할당된 메모리에 들어앉아야 자신의 일을 할 
수 있다. 이 메모리에 들어앉은 프로그램코드의 복사본을 우리는 프로세스라 한다. 어
떤 사람들은 프로세스란 프로그램 코드의 실행중인 인스턴스라고도 한다. 이렇게 말하
면 조금더 추상적으로 들리고, 좀더 그럴듯 하다. 어쨋건, 우리는 이 내용을 새로이 
유권해석해서 더 쉽게 이해하고 있도록 하자.
"프로세스란 프로그램이 실행될때 맨 처음 실행되는 main함수가 실제 구동되고 있는 
상태이다"
헉... 넘 허접한가?
하지만 위의 새로운 정의는 우리가 스레드를 이해하는 데 매우 강력한 역할을 해줄것
이다.
위의 말을 생각해보자. main함수 없이 (윈도우즈 에서는 WinMain 이다) 돌아가는 프로
그램은 존재하지 않는다. 다시말해 실행중인 프로그램은 전부다 프로세스를 하나씩 가
지고 있다는 말이된다. 그리고 그 프로세스란 지금 실행중인 프로그램 자신의 본체이
다. 또, 한 프로그램에서 main함수가 둘 이상이면 컴파일 에러가 난다.(내가 예전에 
도스를 쓰던 당시 이런 프로그램을 만들었던 적이 있다. 내 딴에는 획기적으로 잔머
릴 굴렸던 것이다. 화면을 13h의 그래픽모드로 전환해 놓고 - 이것은 도스운영체제에
서 VGA카드를 프로그래밍해본 분은 알 것이다 - 화면 처리를 하면서 동시에 소수점 연
산을 하는 매우 획기적인 프로그램을 만들고자 했었다... ㅜ.ㅜ;) 즉, 모든 프로그램
은 프로세스를 하나씩만, 반드시 가지고 있다.

이제 프로세스에 대해서 알았으니, 잠시 윈도우즈 운영체제 이야기를 해보자.
윈도우즈에서는 여러개의 프로그램을 동시에 실행상태로 만들어 둘 수 있다. 즉, 여러
개의 프로세스를 메모리상에 띄워 놓는 것이다. 이것을 멀티 태스킹이라 한다. 즉, 멀
티 태스킹이란, 동시에 여러개의 서로다른 프로세스가 구동되는 것을 말한다.
윈도우즈는 어떻게 이 작업을 수행하는가? 바로 CPU의 클럭을 쪼개어 쓴다. CPU가 1초
에 1000개의 명령을 처리할 수 있다고 하면, 이 시간을 실행중인 프로세스만큼 나누
어 쪼개어서 각각의 프로세스에게 얼만큼씩 CPU를 사용하도록 교통정리를 해주고 있다
는 것이다.
어떤 사람들은 윈도우의 이런 멀티태스킹을 진정한 멀티태스킹이 아니라고 한다. 
내 생각에는 윈도우가 이 일을 하지 않았다면, 분명 다른 어떤 운영체제가 이 일을 했
을 것이다. 
진정한 멀티태스킹이란 무엇인가? 실제로 동시에 서로다른 프로세스가 CPU타임을 공유
하는 일 없이 동시에 구동되는 것인데, 이럴려면 마더보드에 최소한 두개 이상의 CPU
가 꽂혀 있어야 한다. 난 한개의 CPU를 사기에도 손이 떨린다. CPU 두개짜리 컴퓨터
를 사기에는 내 형편이 역부족인 것이다. 따라서, 나는 이처럼 동작해서 내 컴퓨터에
서도 멀티태스킹이 가능하도록 해주는 운영체제가 필요하다. 그래서 내 컴퓨터에는 윈
도우즈가 설치되어 있다.
윈도우즈가 이처럼 CPU타임을 나누어서 각각의 프로세스에게 할당하는 것을 프로세스 
스케줄링이라고 한다. 윈도우즈의 프로세스 스케줄링을 생각할 때마다 나는 어떤 무협
지에 나왔던 분신술 이라는 기술이 생각난다. 이 기술이란, 이 기술을 구사하는 사람
이 몸을 매우 재빨리 움직여서, 즉 A라는 포지션과 B라는 포지션을 너무나도 빨리 왕
복하면서 상대방의 눈의 잔상효과를 이용해 자신의 몸이 둘로 복사된 것처럼 보이게 
한다는 아주 무서운 기술이다. 헉.... 이 기술을 구사하면, 상대방이 헷갈릴수는 있겠
지만, 기술을 구사하는 자신은 아마도 기술구사에 너무 지쳐서 상대방은 멍하니 쳐다
보다가 승리를 얻게 될 것이다. 게다가 이론 대로라면, 너무나도 빨리 움직이기 때문
에 상대방은 둘 중의 하나만 공격하더라도, 자신이 와서 맞아준다. 결국, 적은 아무고
민 없이 둘 중 하나만 맘놓고 공격하면 지가 와서 맞아준다는 것이다 ㅡ,.ㅡ;.... 이
런..... 이런 허접한 기술이 있단 말인가? 
우리의 윈도우즈 운영체제는 실제로 이 기술을 구사하고 있다. CPU타임을 나누는 교통
정리 작업이 너무나도 빠르기 때문에 우리는 마우스로 아무 프로그램 윈도우나 콕 찍
으면, 마치 그 프로그램은 잠시도 쉬지않고 입력을 기다려 왔다는 듯이 즉각 반응한
다. 무서운 운영체제다....

그러면 스레드란 무엇인가? 각각의 실행중인 프로세스는 자신의 스레드를 갖는다. 사
실상 위에서 이야기 한 것들이 다 프로세스란 탈을 쓴(프로세스라고 불려지는) 스레
드 인 것이다.
음.... 위에서 한 이야기를 번복해야만 하는가?
사실은 스레드 없이 돌아가는 프로그램은 없다. 응용프로그램이 실행될때 반드시 하나
의 스레드가 시작된다. 이것을 프로세스라고 부르는 것이다(혹은 메인 스레드 라고도 
한다).
각각의 스레드는 자신으로부터 파생된 스레드를 구동시킬 수 있다. 즉, 하나의 프로그
램에서 또하나의 main함수가 시작될 수 있다는 것이다. 반가운 소식이다. 이제, 나의 
도스시절에 작성해 두었던 소스는 실행될 수 있게 되었다.(실제 코드가 둘 이상의 
main함수를 가지고 돌아가는 것은 아니다. 오해 없길 바란다)
우리가 프로세스란 개념으로 이야기 할 때와 스레드란 개념으로 이야기할 때의 차이점
은 다음과 같이 표현할 수 있다.

"모든 실행하는 프로그램은 반드시, 그리고 하나의 프로세스를 갖는다. 반면, 프로그
램은 반드시 하나의 스레드를 갖고,  둘 이상의 스레드를 가질 수 있다"

만일 프로그램 내에서 메인스레드 외에 또 다른 스레드를 구동시켰다고 하자. 그렇다
면, 위의 이야기를 종합해서 분석해 볼때, 우리의 머리는 다음과 같은 결과를 output 
할 것이다.
"서로다른 스레드들이 동시에 실행되는 것처럼 보일려면, 스레드들도 각기 CPU타임을 
나누어 써야만 한다"
이 일을 프로그램의 프로세스가 해주는 것이다. 즉, 운영체제는 프로세스를 스케줄링
하고(이것을 멀티 태스킹이라고 한다), 프로세스는 스레드를 스케줄링한다(이것을 멀
티 스레딩이라고 하는 것이다). 이제, 이 말을 이해할 수 있겠는가?

음..... 마이크로 소프트는 위와 같이 멀티태스킹과 멀티스레딩을 설명하고 있지만, 
사실은 그렇지 않다. 윈도우즈 운영체제는 결코 프로세스를 스케줄링 하지 않는다. 사
실, 윈도우즈는 프로세스가 가지고 있는 개개의 스레드를 스케줄링하고 있는 것이다. 
생각해 보라, 우리가 만든 응용프로그램의 인스턴스인 프로세스가, 우리가 코딩해 주
지도 않은 스레드 스케줄링을 어떻게 스스로 한단 말인가? 우리는 단지 "이 시점에서 
이 스레드를 시작하고, 저 시점에서 그만 두라" 하고 프로그래밍 할 뿐이다. 스케줄링
은 운영체제가 하는 것이다. 프로그래밍의 공력이 증가하고 나면, 지금의 이 이야기
는 매우 중요한 이야기가 될 것이다. 기억해 두는 것이 좋다.

소켓을 통한 전송(거기에 파일 입출력이 함께 동작하고 있는)과 같이 CPU타임을 독차
지하는 작업을 할 때 프로그램은 잠자는 숲속의 공주가 된다. 이를 해결하기 위한 가
장 좋은 방법은 소켓을 위한 스레드를 만들어 구동시키고, 이 스레드를 잠든 것처럼 
만들어 주면 되는 것이다.
이제, 이번 장에서 내가 왜 스레드를 이야기 하는  지, 그리고 멀티스레딩이란 무엇인
지 알았을 것이다. 사실, 우리는 서버측에서 다중접속을 지원하기 위해서도 스레드를 
사용할 것이다. 그래서 스레드에 대한 이해는 중요하다.

2. MFC에서의 스레드의 구동
MFC에서는 두 종류의 스레드를 지원한다. 작업자 스레드(worker thread)와 사용자 인
터페이스 스레드(user interface thread)가 그것이다.
둘의 차이점은 둘이 스레드를 구동시키는 방식이다. 작업자 스레드는 마치 따로 정의
된 함수를 호출하듯이 메인 스레드의 어느 한 시점에서 스레드를 호출한다. 사용자 인
터페이스 스레드는 스레드 객체를 정의하고, 이 객체의 인스턴스를 생성하도록 프로그
램에게 지시하여 스레드를 시작한다. 이것은 MFC에서 제공하는 인터페이스의 차이이
지, 스레드가 근본적으로 두 종류가 있는 것은 아니다.

사용자 인터페이스 스레드는 CWinThread로 부터 상속받은 스레드 객체를 정의한다. 그
리고 이 객체의 포인터를 스레드 구동함수에게 인자로 전달하면서 스레드를 생성한다.
새로운 객체를 정의하는 방식이기 때문에, 우리는 이 객체에서 여러 멤버를 추가할 수
도 있고, 메시지 핸들러를 추가할 수도 있어서, 우리가 마음에 들도록 스레드를 제어
하는 것이 가능하다.(그래서 사용자 인터페이스 스레드라 불린다)
이 쯤 이야기 하면, 여러분은 내가 둘중에 어느 스레드방식을 더 좋아하는지 눈치 채
셨을 것이다. 그렇다.... 사용자 인터페이스 스레드이다.
이 강좌에서는 이 스레드에 대해서만 이야기 하고, 이 스레드만 사용한다.(혹시, 나중
에 맘이 바뀔지도 모르겠지만....)

맨 처음 사용자 인터페이스 스레드를 사용하기 위해서는 CWinThread로 부터 상속된 스
레드 클래스를 만든다.

class CWinThread : public CMyThread

그러면, 만들어진 클래스의 선언부에 위저드가 DECLARE_DYNCREATE(CMyThread)라는 매
크로 호출을 넣어놓은 것을 볼 수 있을 것이다. 이것이 있으므로, 당연히 클래스의 정
의 부분에는 IMPLMENT_DYNCREATE 라는 코드가 있을 것이다.
이것이 있기 때문에 우리는 CMyThread객체를 런타임클래스의 포인터로 캐스트 할 수 
있는데, 자세한 내용은 나도 모른다.(헉....ㅡ.ㅡ;) 어쨌건, 스레드의 구동함수에게 
이 런타임클래스의 포인터를 인수로 전달해 주어야 하는 것이다.

스레드의 구동함수는 Win32 API 함수를 포함해, 몇가지 된다. 그러나 MFC에서 우리는 
단 하나의 스레드 구동함수를 사용할 것이다. 바로 AfxBeginThread 함수이다. 이 강좌
에서는 이야기 하지는 않지만, 여러분은 스레드 구동함수들을 사용하기 전에 이들함수
들과, C++ 라이브러리들과의 호환성을 알아보아야 한다. 우리가 사용하는 
AfxBeginThread함수는 MFC클래스라이브러리와 C런타임 라이브러리와 Win32 API 를 모
두 지원한다. 즉, AfxBeginThread로 구동시킨 스레드 내에서는 이들 라이브러리를 링
크시킬 수 있고, 라이브러리에 있는 함수를 호출하여 사용하고, 구동시킬 수 있다는 
말이다.

앞서 이야기 한대로, 스레드 클래스를 프로젝트에 추가하였으면, 이 클래스의 인스턴
스를 생성한다. 우리는 주 클래스내의 멤버변수로 이 인스턴스를 생성하게 될 것이
다. 다음과 같이..

CMyThread * m_pThread;

여기까지 하면 스레드를 사용할 준비가 된 것이다. 이제 이 클래스의 CRuntimeClass 
* 형으로 캐스트한 포인터를 스레드 구동함수에 넘기면서 스레드 구동함수를 호출하
면, 스레드를 실행할 수 있다.
m_pThread = (CMyThread *) AfxBeginThread((RUNTIME_CLASS)CMyThread);
위와 같이 호출한다. AfxBeginThread는 전역함수로, 스레드 객체를 생성해주고, 그 객
체의 포인터를 리턴한다. 우리의 멤버변수가 이 포인터를 받으므로, 우리는 생성된 스
레드객체를 핸들링할 수가 있게 되는 것이다.
이 함수는 이보다 더 많은 인자를 전달받게 되어 있지만, 위에서 보여준 첫번째 인자
가 가장 중요하다. 나머지 인자는 모두 디폴트 값을 가지고 있다.(솔직히 말하면, 우
리의 예제에서는 이 인자에 디폴트가 아닌 값을 넣어줄 필요가 있어서 이 인자들을 
이 단계에서 설명해야 겠지만, 이 설명은 나중에 코딩할 때 하기로 한다. 나는 지금 
새벽늦은 시간에 퇴근해서 집에가는 길에 겜방에 들러 강좌를 쓰고 있다. 허접한 나
의 머리는 지금 이 함수의 모든 인자를 기억해 내지 못하고 있다.... ㅡ.ㅡ; 각자가 
MSDN을 참고해서 공부해 주길 바란다)

이제 우리는 스레드를 구동하는 방법까지 알아보았다. 지금은 안개속에서 앞을 보려
고 하는 것처럼 가물가물해도 걱정할 것은 없다. 코딩하면서 나는 지속적으로 입에 침
을 튀겨가며.. 아니 손가락에 쥐를 내가며 설명할 것이다. 그 때에는 보다 더 명확히 
이해할 수 있으리라 확신한다.

내 손가락은 이쯤에서 이번 장을 마쳐주기를 나에게 호소하고 있다. 사실은 스레드의 
동기화에 관해서 여기 덧붙여 주어야 이 장이 처음 의도대로 완벽해 질 것이다(함수설
명하면서 인자에 대한 설명을 빠뜨리는 허접한 부분을 제외한다면... 그런대로 괜찬
은 장 이였다.....)

자... 큰 맘 먹고, 계속해서 스레드의 동기화에 대해 알아보자.(...난 지금 너무 피곤
하다... )
동기화란 무엇인가?? 우리가 소켓객체를 이야기 할때에 소켓객체도 동기소켓과 비동기
소켓이 있다는 것을 이야기한것을 기억하고 있을 것이다. (그 때는 동기와 비동기에 
대한 접근 개념이 여기서와는 좀 달랐다)
프로세스 내에서 생성되는 모든 스레드는 기본적으로 비동기적이다. 즉, 스레드 서로
간에 완전히 독립적이며, 하나의 스레드는 자신 외의 다른 스레드의 동작에 대해 전
혀 무관심하다. 이것은 스레드의 개념에도 부합된다. 스레드가 스레드 다울려면, 당연
히 비동기 적이어야 하는 것이다. 그런데, 스레드가 비동기 적이라는 데서 문제가 생
기는 경우가 종종 있다. 
이를 테면, 하나의 스레드에서 어떤 파일을 열고 쓰기를 시도하고 있다. 그런데, 다
른 스레드가 또 이 파일을 열고 쓸려고 한다. 혹은, 어떤 프로세스내의 변수를 어떤 
스레드가 조작해서 값을 변경시키려고 한다. 그런데, 다른 스레드에서는 이 변수의 값
을 참조하는데, 다른 스레드가 값을 변경시켰다는 것을 전혀 모른다.. 
이런경우에, 시스템 서비스를 콜하는 부분에서 충돌이 일어나서 프로그램이 이해할 
수 없는 글들이 적힌 메시지 박스를 띄우면서 종료되거나, 프로그래머가 전혀 예상하
지 못한 황당한 동작을 수행하거나 하는 등의 문제가 발생하게 되는 것이다.
이런 상황이 예상되는 부분, 즉, 서로 다른 스레드가 프로그램의 동일한 리소스를 참
조해야 하는 경우에는 적절히 교통정리를 해 주어야만 문제를 예방할 수 있는데, 이 
교통정리를 스레드 동기화라고 한다. 그러면, 이것은 어떻게 구현할 수 있을까??
MFC에서 스레드의 동기화를 구현하기는 누워서 떡먹기이다. MFC가 스레드를 동기화할 
수 있도록 동기화 객체 클래스를 제공하고 있기 때문이다.(헐.... MFC에는 없는게 무
엇인가??)
스레드 객체에는 4가지가 있다. 뮤텍스, 크리티칼섹션, 세마포어, 그리고 이벤트객체
가 그것이다. 여러분들은 전례를 보아 눈치채셨겠지만, 이 강좌에서는 역시나 이 4가
지를 다 설명하지 않는다. 이 4가지 객체는 각기 용도가 다르다. 사용하게 되는 경우
가 달리 정해져 있는 것이다. 자세한 내용은 스레드에 관한 다른 강좌나 서적 등을 참
조하기 바란다.
우리는 크리티칼섹션에 대해서만 알아보자. 크리티칼섹션을 위한 객체는 
CCriticalSection 이라고 하는 객체이다. 이 객체를 사용하기 위해서는 먼저 이 객체
의 인스턴스를 선언하는 데,
CCriticalSection g_CriticalSection;
과 같은 형태로 전역변수로 선언하거나, 혹은 
CCriticalSection * m_pCriticalSection;
과 같이 포인터형 멤버변수로 선언해두고 이 객체를 사용할 다른 클래스들이 이 포인
터를 참조할 수 있도록 해준다. 이 객체를 사용할 모든 스레드가 동일한 동기화객체
를 사용해야만 한다.
위와 같이 선언했다면, 이 객체의 멤버함수 Lock(); 과 Unlock();을 사용하여 크리티
컬섹션을 지정할 수 있다. 예를 들어 A라는 스레드에서 AWork라는 동작을 하고, B라
는 스레드에서 BWork라는 동작을 하는 데, 이 동작들이 같은 리소스를 참조해야한다
고 할 때, 하나의 스레드가 이미 그 리소스를 참조 중이면 다른 스레드는 그 작업이 
완료될 때까지 기다리게 할 수 있다. 다음과 같이 한다.

// 스레드 A 에서...
.......
m_pCriticalSection->Lock();

작업 AWork;

m_pCriticalSection->Unlock();
.......


// 스레드 B 에서도.....
.......
m_pCriticalSection->Lock();

작업 AWork;

m_pCriticalSection->Unlock();
......

위와 같이 스레드 A와 스레드 B에서 각기 Lock();과 Unlock();함수로 같은 리소스를 
참조하게 되는 부분을 둘러 싸 놓으면, A에서 해당 작업이 수행되는 동안은 B에서는 
Lock(); 함수에 걸려서 대기상태가 되고, A에서 Unlock(); 함수가 호출 되어야만 스레
드 B의 작업이 진행된다. 스레드 B에서 작업중이라면, 스레드 A가 똑같이 대기상태가 
된다. 따라서, 서로 다른 스레드가 같은 리소스를 결코 동시에 참조하지는 못하게 되
는 것이다. 일종의 안전장치이다.

휴..... 이제 3장을 마쳤다. 본론에 들어가기 위한 준비를 마친 것이다. 여러분은 이
제, 본론의 코딩작업에 들어가기위한 제반 지식을 갖추었다. 지금 까지의 과정에서 이
야기 해야하는 데 이야기 하지 못하고 넘어간 많은 부분들을 나는 잘 알고 있다. 
2장에서는 소켓객체에서 오버라이딩 해 주어야 하는 이벤트 핸들러 들과, Receive함수
와 Send함수, 3장에서는 생성하고 구동시킨 스레드를 제어하는 몇가지 추가적인 스레
드객체의 멤버함수들...
이들은 코딩부분을 작업하면서, 만나게 되는 단계에서 다시 자세히 설명하겠다. 여러
분이 이 강좌를 소화하는 데는 아무 문제가 없을 것이다.

그럼, 다음장을 기약하자..........