─━ IT ━─

윈도우 스레드풀링

DKel 2021. 8. 16. 00:38
반응형
처음에 스레드 풀링이란, 복수의 실행 스레드를 관리하고, 처리를 각 스레드로 분산시키기 위한 테크닉이다.경우에 따라서는 동시 실행 제어 등의 기능도 쓰레드풀링의 일부로 생각된다.스레드 풀링은 이하의 처리를 작업하기에 적합한 방법이다.스레드 풀링은 스테이트 베이스의 처리에 적합하다.시스템을 몇개의 스테이트 머신으로 분해할 수 있는 경우에는, 스레드 풀링을 이용해 그 시스템을 효과적으로 실현할 수 있다.여기에는, 대부분의 경우에 멀티 스레드 애플리케이션의 디버깅이 단순화 된다고 하는 부차적인 메리트도 있다.올바르게 실장하면, 스레드 풀에 의해서 동시 실행의 제한을 마련해 애플리케이션의 확장성을 높일 수 있다.쓰레드 풀링을 이용하면, ′샌드 박스′라 불리는, 시스템의 실행을 몇개의 작은 단위로 분할하는 시큐리티 모델을 실현할 수 있다.′샌드박스′ 모델에서는 스레드 풀링에 의해 프로세스 간의 결합이 느슨해지고, 프로세스로부터 데이터가 독립하기 때문에 안전하다.이 방식으로는 프로세스끼리 복잡하게 얽히는 것이 아니라 점에서 접하게 된다.이로 인해, 보수의 수고가 큰폭으로 감소한다.특히, 대규모 멀티 스레드 애플리케이션에서는 효과가 크다.설계 스레드풀의 개념 모델은 단순하고 스레드풀이 스레드풀을 시작하다
처리가 스레드풀의 큐에 넣히다.
큐내의 처리가 사용가능 스레드에서 실행되는 형태이다.템플릿을 사용하면, 풀을 스레드/처리의 실장으로부터 독립시킬 수 있다(이 테크닉은 정적 폴리모피즘이라고 불린다).쓰레드 풀은 쓰레드의 작성을 담당한다.쓰레드는 worker :: thread_proc에 의해서 실행을 개시한다.요청은 쓰레드풀의 큐에 넣는다.이 때, 워커가 리퀘스트의 준비를 하고, 리퀘스트를 큐에 넣는다.스레드가 처리를 수행할 수 있는 상태가 되었을 때, 스레드가 thread_pool :: get_queued_status를 사용하여 스레드 풀에 대기 중 처리를 요청한다.대기중의 처리가 없는 경우는, 처리가 송신되어 올 때까지, 쓰레드는 휴지 상태가 된다.체이닝 이 워커 실장에 의해서 처리를 큐화할 수 있지만, 한 걸음 더 나아가 보자.이 쓰레드 풀은 문제를 개별 단계로 분해하는 데 도움이 되며, 이를 통해 시스템의 능력을 최대한 활용하면서 복잡함과 위험을 최소화할 수 있다.그러나, 현재의 실장으로는 한 번에 1개의 처리밖에 큐화할 수 없기 때문에, 일련의 처리를 논리적으로 정리하는 것이 어렵다.게다가 후속 처리를 큐화하기 위해서는 처리가 언제 실행되었는지를 알 수 있는 수단이 필요하다.시스템을 다운시켜 시스템 데이터를 재구축하고 시스템을 다시 온라인으로 하는 일련의 처리가 있다.이것은 3개의 스텝, 3개의 처리이다.이를 다음과 같은 형태로 구현한다.thread _ pool :: instance ( ) . queue _ request (
( core :: chain ( ) , new system _ down , new rebuild _ data ,
new system_up)); 여기서 나온 체인(chain)이란 일종의 처리 단위이다.정확하게는, 이 체인은, 실제의 처리 단위(실은 처리 단위의 컨테이너)의 컨테이너로서만 기능한다.struct chain {
struct data : work _ unit , std :: list < smart _ pointer < work _ unit > > {
void process ( ) ;
};

chain ( ) : m _ work ( new data ) { }
chain & operator , ( work _ unit * p _ work ) ;
operator work _ unit * ( ) { return m _ work ; }
smart _ pointer < data > m _ work ;
}; //struct chainchain::operator는 다음의 처리만 수행한다.m _ work - > push _ back ( p _ work ) ;
return *this;chain ::data::process()는 다음 처리만 한다.front ( ) - > process ( ) ;
pop _ front ( ) ;

// if not empty , requeue
if ( true == empty ( )) return ;
thread_pool :: instance() .queue_request(this); 코드 사용방법 우선 사용할 쓰레드 풀을 초기화한다.쓰레드 풀은 파라미터가 있는 싱글톤이므로 사용하는 워커의 종류마다 하나의 쓰레드 풀 인스턴스가 존재한다.global :: thread_pool 클래스는 core :: thread_pool 를 나타내는 편리한 typedef이다.global :: thread_pool :: instance() .initialize(); core :: worker_thread 를 워커 장착으로 사용할 경우, 모든 처리가 core :: work_unit 에서 파생되게 되며, 이 프로세스가 호출되었을 때 처리가 실행된다.struct mywork : core :: work _ unit
{
void process ( ) throw ( )
{
// work is processed here
}
}} 처리를 큐화하려면 클래스의 Instance를 작성하고 필요에 따라 초기화 한다.이 처리를 thread_pool :: queue_request를 사용하여 큐화한다.// demonstrate chaining
global :: thread _ pool :: instance ( ) . queue _ request (
(core:: chain(), new work_1, new work_2, new work_3); 스레드 풀을 셧다운하려면 thread_pool: shutdown을 사용한다.global::thread_pool:: instance() .shutdown(); 다른 스레드 풀을 작성하려면 워커 스레드와 그 구성 부품을 정의하면 된다.워커 스레드에서는 request_type, prepare_request, thread_proc를 반드시 정의할 필요가 있다.thread_proc에 전달되는 LPVOID 파라미터는 반드시 0으로 사용해서는 안 된다.이 파라미터를 통해 콘텍스트를 제공하도록 쓰레드 풀을 확장할 수 있다.다음 코드에서는 io:: thread와 io:: thread_pool을 정의하고 있다. // sample io worker thread
namespace io {
struct thread
{
typedef io :: session request _ type ;
static void prepare _ request ( request _ type * ) throw ( ) ;
static void thread _ proc ( LPVOID ) throw ( ) ;
} ; // struct thread

struct thread _ pool : core :: thread _ pool < io :: thread > {};
} // namespace io 사용자는 io::thread_pool::instance();를 사용하여 io::thread_pool에 접근할 수 있다.데모 프로그램에 대해 데모 프로그램은 다음과 같다.global :: thread_pool 인스턴스의 초기화
세 가지 처리의 인스턴스화
처리를 정리하는 체인의 인스턴스화
처리 큐화 및 실행
쓰레드풀의 셧다운 팀에서 개발하고 있는 경우는 큰 처리를 작은 처리 단위로 간단히 분할하여 팀 내에서 분배할 수 있다.각각의 처리단위는 독립적으로 테스트할 수 있고 또한 최종적인 제품으로서 통합할 수도 있다.각각의 개발자는, 담당 범위내에서 자신의 일을 완수하면 되는 것이다.스레드풀은 확장성과 성능이 뛰어난 대규모 시스템을 단기간에 안전하게 개발할 수 있는 훌륭한 도구이다.꼭 시험해 주었으면 한다.
반응형