3장에서 만든 TCP 에코 서버는 동시에 하나의 클라이언트에게만 서비스를 제공할 수 있습니다. 하나의 서버가 동시에 여러 클라이언트에게 서비스를 제공하는 것을 멀티플렉싱(Multiplexing)이라 부릅니다.
이번 장에서는 스레드를 이용하여 멀티플렉싱을 구현하는 방법을 알아보기로 합시다.
스레드란 하나의 프로세스 내에서 독립적인 수행할 수 있는 작업 단위입니다.
예를 들어 파일 공유 P2P 프로그램을 사용할 때 동시에 여러 개의 파일을 다운로드를 받는 것은 스레드를 사용한 대표적인 예입니다.
스레드를 시작하는 함수 #include <process.h> unsigned int _beginthread (void (* _ThreadEntryPoint) (void *),unsigned _stacksize, void * param);
스레드를 시작하는 함수에는 C 라이브러리 함수인 _beginthread, _beginthreadex외에도 Windows API 함수인 CreateThread 등이 있습니다. 이 책에서는 C 라이브러리 함수인 _beginthread를 이용할게요. 다른 스레드를 시작하는 함수도 입력 인자로 스레드 진입점 함수명(함수명은 코드 메모리 주소를 의미함)와 진입점에 전달할 인자입니다.
_beginthread 함수도 첫번째 인자로 스레드 진입점을 입력 인자로 받으며 세번째 인자로 진입점에 전달할 인자를 받습니다. 그리고 두번째 인자는 스레드의 스택 크기인데 0을 전달하면 디폴트 크기를 사용합니다.
스레드 진입점은 다음의 예처럼 입력 인자가 void 포인터를 받는 함수로 정의해야 합니다.
void ThreadEntryPointA(void *param) { for(int i = 100; i<110;i++) { Sleep(rand()%100); cout<<i<<endl; } }
다음 예제는 main(프로세스 진입점, 메인 스레드 진입점이라도 부름)에서 서로 다른 두 개의 스레드를 시작한 후에 반복문에서 랜덤한 시간동안 멈추고 0에서 9까지 출력하고 엔터를 누르면 종료하도록 작성한 코드입니다. ThreadEntryPointA 스레드 진입점에서 100에서 109까지 출력하고 ThreadEntryPointB 스레드 진입점에서는 200에서 209까지 출력하게 작성하였습니다.
//스레드 예1 #include <iostream> using namespace std; #include <process.h> #include <Windows.h> void ThreadEntryPointA(void *param) { for(int i = 100; i<110;i++) { Sleep(rand()%100); cout<<i<<endl; } } void ThreadEntryPointB(void *param) { for(int i = 200; i<210;i++) { Sleep(rand()%100); cout<<i<<endl; } } int main() { _beginthread(ThreadEntryPointA,0,0); _beginthread(ThreadEntryPointB,0,0); for(int i = 0; i<10;i++) { Sleep(rand()%100); cout<<i<<endl; } cin.get(); }
이 프로그램을 실행하면 main 스레드 외에 두 개의 스레드가 동작하며 서로 간섭없이 독립적으로 동작합니다. 실제 출력 결과는 매 번 다를 수 있으며 다음처럼 세 개의 스레드(main 스레드와 main에서 가동한 두 개의 스레드)의 작업은 서로 간섭없이 동작함을 확인할 수 있습니다.
그리고 다음처럼 스레드에 인자를 전달할 수도 있습니다. 주의할 점은 스레드마다 독립적인 스택을 갖으므로 지역 변수의 주소를 인자로 전달하지 말아야 합니다.
//스레드 예2 #include <iostream> using namespace std; #include <process.h> #include <Windows.h> void ThreadEntryPointA(void *param) { for(int i = 100; i<110;i++) { Sleep(rand()%100); cout<<i<<endl; } } void ThreadEntryPointB(void *param) { for(int i = 200; i<210;i++) { Sleep(rand()%100); cout<<i<<endl; } } int main() { _beginthread(ThreadEntryPointA,0,0); _beginthread(ThreadEntryPointB,0,0); for(int i = 0; i<10;i++) { Sleep(rand()%100); cout<<i<<endl; } cin.get(); }