리눅스 C++용 유저레벨 쓰레드 라이브러리
CSThread Library 20060221
작성자 : umean2me@bcline.com
작성일 : 2006년 2월 21일
* CSThread에 포함된 파일
CSTRunnable.h DeadQueue.h Mutex.h Task.h UTracer.h
CSTSignal.h ICSThread.h ReadyQueue.h TaskScheduler.h WaitingQueue.h
CSThread.h ITask.h RunningQueue.h TimeSpec.h libcsthread.a
cstest.cpp cstest Makefile
* 컴파일 환경
OS : Red Hat 3.4.2-6.fc3
Kernel : 2.6.9-1.667 (Intel 336/128M)
Compiler: gcc version 3.4.2 20041017 (Red Hat 3.4.2-6.fc3)
아직 소스는 공개는 아직 이르다고 생각됩니다. 헤더를 보시면 아시겠지만 워낙 ............. 기본테스트 기능 구현에만 그치고 있기 때문입니다. 따라서 아래와 같은 라이센스를 가지고 있으며 스터디 하고자 하는 분들께 또 저와같은 입장의 분들께 스터디가 되었으면 좋겠습니다. 관심있는 분들의 많은 연락 부탁드립니다.
* 라이센스
이 라이브러리는 나(umean2me@bcline.com)에게 있으며 상업적 용도로 사용, 배포 수정등 아무 행위도 할 수 없습니다. 오직 스터디 용도로만을 허락합니다. 또 저작자인 나는 이 라이브러를 사용함으로써 생기는 모든 손해, 불이익등의 법적,도덕적,기타등등의 모든 책임을 지지 않습니다.
* 동기
- Unix C++을 지원하는 유저레벨 쓰레드 라이브러리는 거의 없다시피 합니다. 대게 있다해도 그전 소스를 Wrapping한 것에 불과하거나 또는 C++지원을 하지 않았습니다.
- 현존하는(?) 유저레벨 쓰레드는 다수의 커널레벨 쓰레드상에서 재진입이 불가능 합니다.(커널레벨쓰레드+유저레벨쓰레드 조합시)
- 1개의 클라이언트당 100개의 이벤트가 존재하고 이 클라이언트가 1000여개 존재해야만 할 때 CallBack으로는 설계/프로그래밍/유지보수가 까다롭고 시간이 오래걸립니다. 또는 쉽게 처리하기 위해 1000여개의 커널 쓰레드를 띄운다면 쓰레드에 대한 처리 비용이 대부분입니다.
이러한 부분을 해소하기 위해 오버헤드가 적고 커널레벨 쓰레드상에서 안전하며 C++을 사용하는 유저레벨의 쓰레드가 필요 했습니다.
* 특징
- 컨텍스트 기반으로 작성되었습니다.
- JAVA스타일과 엇비슷한 C++ 인터페이스를 제공합니다.
JAVA처럼 Runnable 상속으로 쓰레드를 만듭니다. 함수수준의 호출보다 훨 씬 더 직관적이라고 생각되며 사용시에도 상당히 편리합니다.
- 재진입 함수를 요구하지 않습니다.
라이브러리상에서 제공하는 메소드를 통해 쓰레드가 리턴되기를 기다리기 때문에 중간에 인터럽트되는 일이 없습니다. 때문에 재진입에 항상 안전하게 됩니다. 이는 비선점형으로 스케쥴링 되기 때문이기도 합니다.
- 이 라이브러리는 다수 커널레벨에서 안전할 수 있습니다.
대개의 유저레벨 쓰레드 라이브러리들은 다수의 커널쓰레드 상에서 오류를 발생하고 프로그램이 종료됩니다. 이것은 전역 또는 static 변수들을 사용하기 때문인것으로 생각됩니다. 이 라이브러리는 변수등의 사용에 클래스 안으로 한정하여 쓰레드 객체를 다른 커널레벨 상에서 운영하더라도 락만 사용하여 준다면 안전합니다.
- 대형 수치 연산에 적합하지 않습니다.
비선점형으로 운영되기 때문에 한 쓰레드가 수치연산을 위해 CPU를 오랜시간 점유할 수록 다른 쓰레드의 실행이 지연되기 때문입니다. 이 때에는 병행성을 확보해야 하기 때문에 선점형 쓰레드를 사용하여야 합니다. 하지만 이벤트 기반의 프로그램에서는 많은 이벤트를 가지며 짧은 처리시간으로 설계, 유지보수등의 많은 부분에서 이득을 볼 수 있습니다. 저 또한 서버어플리케이션을 제작하기 위해 만든 라이브러리 입니다.
- 안정성이 확보되지 않았습니다.
여러가지 테스트는 해 보았으나 다양한 환경이나 프로젝트에 적용된 적이 아직 없습니다. 또 퍼포먼스등의 비교테스트도 하지 못한 실정입니다.(C 라이브러리를 Wrapping하려니 여간 귀찬은 작업이 아니라서......)
- 인터페이스에서 지원하는 메소드가 거의 없습니다.
많은 부분에서 일반적인 쓰레드 메소드들을 제공하지 않습니다. 이는 클래스라는 장점으로 C처럼 여러가지 API를 필요로 하지 않는다고 판단하였습니다. 또 기본적인 인터페이스 만으로 여러가지 상황에 대처할 수 있다고 판단되기도 합니다. 하지만 아직 쓰레드 정보등의 처리에서 부족한 것은 사실입니다.
* 사용자 인터페이스
CSThread
CSTRunnable로 만들어진 쓰레드를 실행시키며 메인 쓰레드로써의 역할을 수행합니다. 제공되는 인터페이스는 ICSThread를 상속받아 CSTRunnable의 인터페이스와도 같습니다.
CSTRunnable
이 클래스를 상속받아 쓰레드를 구현 할 수 있습니다. 먼저 가상함수로 된 void run() 메소드를 구현해야 합니다.
ICSThread
ICSThread는 쓰레드와 스케쥴러, 태스크와 통신하기 위해 실행모듈과 사용자 사이에 위치한 인터페이스입니다. CSTRunnable과 CSThread는 이 클래스를 상속 받았기 때문에 제공되는 wait와 signal 두개의 메소드의 사용이 가능합니다.
wait함수를 호출 할때 인자로 대기시간값을 받는데 그 때에 다른 쓰레드에게 그 처리를 넘겨주게 된다. 따라서 wait를 사용하지 않는다면 전체 쓰레드는 원활히 진행 될 수 없습니다.
쓰레드의 종료
CSTRunnable의 run() 메소드에서 빠져나오는 순간 쓰레드 스케쥴러는 이 쓰레드를 죽은 쓰레드로 간주합니다. 하지만 자원은 아직 되돌려 받지 못합니다. 이 쓰레드를 delete하게 되면 모든 시스템자원을 자동으로 돌려주게 됩니다.
기타 클래스들(...........)
위의 쓰레드를 사용하는데 있어 3개의 클래스를 작성하여 사용하였는데 임시적인 클래스라 거들떠 보지 않는것이 좋으며 꼭 필요한 부분만을 대충 작성한것도 아닌것도 있고 아닌것도 있고 그렇습니다....ㅜㅜ..
* 소스코드 작성 예제
#include "CSThread.h" using namespace std; using namespace csthread; //쓰레드 클래스 class MyCstRunnable : public CSTRunnable { public: MyCstRunnable(int id) : id_(id), i_(0) {} virtual ~MyCstRunnable() { printf("UT Destroy : %d\n", id_); } void run() { printf("UT START : %d\n", id_); while(1) { //1초동안 대기 wait(1000); printf("UT[%03d] : i = %d\n", id_, i_++); } } private: int id_; int i_; }; int main(void) { int i = 0; //메인 쓰레드 생성 CSThread mainThread; MyCstRunnable *myCstRunnable[500]; //쓰레드 생성 및 실행 for (int i = 0 ; i < 500; ++i) { myCstRunnable[i] = new MyCstRunnable(i); mainThread.safeRun(myCstRunnable[i]); } while(1) { //1초동안 대기 mainThread.wait(1000); printf("MT : i = %d\n", i); ++i; if (i == 10) break; } //쓰레드 삭제 for (int i = 0; i < 500; ++i) delete myCstRunnable[i]; }
* 참고
이벤트에 대한 프로그래밍에서 일반적으로 CallBack구조를 많이 사용합니다. 한 비지니스 또는 개체에 대한 프로세싱 과정에서 다양한 이벤트들이 나타나게 됩니다. 이 이벤트들은 각기 종류도 다르며 때때로 이미 발생했어야 할 시기를 지났을 때도 있습니다.
이러한 예측 가능한 이벤트 발생과정에 대하여 여러 조건들을 충족시키려 할때 CallBack 구조에서는 이벤트마다 끊어진 프로세싱을 여러가지 변수등을 통해 이어주어야 합니다. 때때로 작은 MDB라이브러리를 이용할 때도 있지만 MDB를 이용한다 하더라도 이벤트의 종류가 많아질 때마다 처리는 더욱 복잡해져만 가게 마련입니다.
이러한 복잡도 증가는 오류와 유지보수의 크나큰 약점을 가지고 있으며 작은 변화에도 빠르게 대체하지 못하게 됩니다. 단, 한 프로세스가 단 한개의 클라이언트만 처리한다면 그리 문제되지도 않겠지만 수개에서 수천개에 이르는 클라이언트를 처리한다면 앞서 말한만큼 이상의 문제가 되기 마련입니다
비록 CallBack 구조로 하여금 시스템이 그나마 단순해지긴 했으나 내 경험상으로 아직 유지보수, 오류 발생 빈도, 정확한 구현에서 만큼은 충족시키지 못하고 있다고 생각됩니다. 그래서 생각한것이 유저쓰레드를 두어 해결하는 것입니다. 물론 그전의 유저쓰레드 라이브러리들이 이미 존재하나 C수준이며 또한 두개의 커널쓰레드 위에서는 오류를 발생하고는 다운되는 현상이 계속 발생하였습니다.
이러한 문제점을 없애고자 유저레벨 쓰레드 라이브러리를 만들어 보았습니다. 이 쓰레드 라이브러리는 소스의 크기는 작지만 아직 구현단계에 머물러 있어 빠르고 작지 않습니다.(구현 테스트에서만 머무름.)
참고 : GNU Portable Library, State Thread Library, Reactor Pattern
첨부 | 파일 크기 |
---|---|
CSThread_20060221.tgz | 52.04 KB |
댓글 달기