About ThreadPool~
이번에는 ThreadPool에 대해서 소개할까 합니다. 혹시 이 부분에 대해서 잘 알고 계신가요? 아마 잘 알고 있다 보다는 “음~ 들어는 봤고, 대충 무엇인지는 알아!”라고 답 하시는 분들이 많을 것이라고 대략 추측해봅니다. 그런데, 이번에는 “대충 무엇인지는 알아!” 보다는 좀 더 자세하고, 세밀한 부분을 설명하고자 합니다.
솔직히 말씀 드리면, 본인이 이 ThreadPool에 흥미를 가져 소개하려고 할 때에, 참고하던 자료 중에 제가 좋아라 하는 Jeffery Richter가 쓴 MSND Magazine Article이 있더라구요. Jeffery Richter의 글은 MSDN 등에 잘 언급되지 않은 내용들을 깊숙이~ 잘 소개하기로 나름 유명하신 분이며, 또한 .Net 분야에 상당히 내공이 깊어 많은 분들로부터 인정 받고 있기 때문에, 참고하지 않을 수 없었습니다. 그래서 이번 소개 글은 이 Article의 번역 본이라기 보다는, 번역본을 조금 더 이해하기 쉽게 또는 읽기 쉽게 소개하고자 하는 글이니 이점 참고하시기 바랍니다.
그럼 이제 대략 이 글에 대한 앞선 설명을 드렸으니, 이제부터 한번 시작해 보겠습니다.
여러분은 이번 주제인 ThreadPool이 왜 생겨났는지에 대한 내용을 혹시 들으신 적이 있으신가요? 혹은 ThreadPool이 어떤 기능을 수행하는지 잘 알고 계시나요? 이 질문에 대해서 정확히 아시는 분은 이 후의 글은 pass하셔도 상관 없겠습니다.
Jeffery Richter에 의하면 Microsoft는 수년 전부터(지금으로부터 따지면 상당히 오래 전에…) Thread가 개발자로 하여금 얼마나 잘 사용되어 지고 있는지 연구했다고 합니다. 그래서 이 결과를 분석하기 위해 조사를 시작했는데, 그 결과는 많은 개발자 들이 어플리케이션에서 발생하는 단일 작업을 처리하기 위해 새로운 쓰레드를 생성하고 작업이 완료되는 시점에서 생성된 쓰레드를 종료 시키는 일들이 빈번하게 발생하는 거였습니다.
그래서 Microsoft는 생각했답니다. 이미 생성된 쓰레드는 종료시키지 않고 다만 상태를 중지 시킨 후에 다음 요청이 들어오면 중지된 상태의 쓰레드를 다시 활성화 시켜 재사용 하도록 말이죠.
그렇지 않을 경우에, 어플리케이션에서 발생하는 작업을 처리하기 위해 쓰레드를 생성할 때 OS 레벨에서는 kernel 객체의 초기화, 그리고 쓰레드에서 사용되는 stack 메모리의 할당 및 초기화, Windows 운영 체제가 새로운 쓰레드가 생성 될 때 로드 된 assembly에게 DLL_THREAD_ATTACH라는 메시지를 보내기 위해 메모리에 로드 된 페이지의 손실 발생.. 등등의 비 효율적인 일들이 벌어집니다.
그리고 나서 쓰레드가 동작을 완료하게 되면, 그때서야 여기에 할당 된 stack 메모리와 kernel 개체의 자원이 모두 해제됩니다.
이런 동작들을 통한 쓰래드의 생성과 소멸은 당연히 많은 오버헤드를 가져오게 됩니다.
이런 연구의 결과로 Microsoft는 Thread Pool을 만들게 되었고, 처음으로 Windows 2000에 적용이 되었습니다. 또한 .Net Framework 팀이 CLR을 설계할 당시에도 그들은 이 Thread Pool을 CLR 안에 탑재하기로 했답니다. 그 결과로 .Net Framework를 통한 managed 어플리케이션은 Thread Pool의 장점을 그대로 사용할 수 있게 되었을 뿐만 아니라 어플리케이션이 동작하는 운영 체제가 굳이 Windows 2000이 아닌 Windows 98과 같은 하위 버전일지라도 managed 어플리케이션은 Thread Pool을 사용해서 쓰레드를 관리하게 됩니다.
CLR이 초기화 될 때, ThreadPool은 아무런 Thread도 생성해서 가지고 있지 않습니다. 어플리케이션이 실행될 때 필요한 쓰레드를 ThreadPool에 요청하면 ThreadPool은 그때서야 새로운 쓰레드를 하나 생성하고 어플리케이션에 할당합니다. 그 이후부터 쓰레드가 작업을 완료하게 되면 바로 종료하지 않고 ThreadPool에 다시 반환되어 중지 상태로 ThreadPool에 머물게 됩니다.
그리고 또 다시 어플리케이션으로부터 쓰레드 요청이 들어오면, 이번에는 새로운 쓰레드를 생성하는 것이 아니라 ThreadPool에 의해 중지 상태에 있는 쓰레드를 활성화 시켜 재사용하도록 합니다. 이런 프로세스는 많은 오버헤드를 감소시킬 수 있습니다.
그런데 이번엔 조금 다른 경우를 한번 생각해 보죠. 어플리케이션이 ThreadPool에 의해 재사용되는 쓰레드를 사용하기 위해서, 오랫동안 단위 작업(task)들을 계속해서 큐(queue)에 쌓게 된다면 이것은 매 작업마다 쓰레드를 할당해서 처리하는 경우 보다 성능 면에서는 느릴 수 밖에 없을 것입니다.
하지만 이 경우는 어플리케이션이 실행되는 동안 ThreadPool에 생성되어 있는 쓰레드를 계속해서 재사용하기 때문에, 발생할 수 있는 엄청난 오버헤드를 감소시킨다는 잇점이 있습니다.
그런데 어플리케이션이 큐에 쌓여 있는 작업들을 각각의 쓰레드를 이용해서 처리하는 것 보다 빠르게 실행시키게 하려면, ThreadPool에 더 많은 쓰레드를 추가로 생성하면 될 것 같지만, 이렇게 새로운 쓰레드를 추가로 생성한다면 오버헤드가 증가되는 결과는 뻔 할 것입니다. 그렇겠죠?
그러나 어플리케이션은 자신이 실행되는 동안 처리해야 하는 모든 작업을 완료하기 위해, 우리 의생각과는 다르게 단지 몇 개의 쓰레드만을 요청하기 때문에 별로 문제 될 것이 없다고 합니다. 이런 점으로 미루어 봤을 때, ThreadPool을 사용하게 됨으로써 어플리케이션의 성능은 전반적으로 향상된다는 결론을 내릴 수 있습니다.
그런데, 위에서 한 말처럼 ThreadPool에 여러 개의 쓰레드를 생성해서 사용했다고 치죠.
만약 어플리케이션이 처리해야 할 작업의 수가 줄어 든다면, ThreadPool에 있는 여러 개의 쓰레드 들은 어떻게 될까요? 쓸 일도 없는데, 쓰레드를 종료시키지 않고 보관하고 있다면 이건 분명 리소스 낭비 밖에 안 될 것입니다. 실제 그렇다면, 음~ 문제입니다.
하지만 이러한 문제도 이미 Microsoft는 고려했다고 합니다. 이러한 경우, ThreadPool은 휴면 상태에 있는 쓰레드를 약 40초 동안 유지하고 있습니다. 그리고 40초가 경과했는데도 어플리케이션이 쓰레드를 요청하지 않아, 계속해서 정지 상태에 있어야 한다면, ThreadPool은 이러한 조건을 만족하는 쓰레드를 종료시켜 버린다고 합니다.
이렇게 됨으로써, stack 메모리와 kernel 생성에 소비되었던 리소스는 해제되어 사용 가능한 리소스가 더 늘어나게 됩니다.
그런데 ThreadPool을 설명하는 원문 글에서 Jeffery Richter는 쓰레드가 유휴 상태로 유지되는 40초라는 시간은 어느 문서에도 명시되어 있지 않는 시간이라고 이야기 합니다. 나중에 확인되면 수정한다고 하네요.(대략 개념을 정리하는데 도움을 주기 위해 사용된 수치 같습니다.)
이 글의 원문은 MSDN Magazine 2003년도 6월에 실린 “The CLR’S Thread Pool”이란 제목의 글이며, 주소는 아래에 적습니다. 이 글에는 이런 내용을 기반으로 몇 가지 더 설명하는 글이 있으며, 예제도 같이 포함되어 있는데, 이 내용은 다음에 전해 드리기로 하겠습니다. 이번에는 살짝 개념만 이해하고 계시면 될 것 같네요. 그럼 저는 여기서 이만 마치겠습니다.
감사합니다. ^o^v
*원문 글
http://msdn.microsoft.com/msdnmag/issues/03/06/NET/
Posted by zmeun
이 글은 스프링노트에서 작성되었습니다.
'닷넷 프레임워크' 카테고리의 다른 글
HEX문자열을 실제 HEX값으로 변환 (0) | 2009.04.27 |
---|---|
Visual Studio 2005 단축키 (0) | 2009.04.27 |
background (0) | 2009.04.27 |
3. 스레드 생성 및 종료 (0) | 2009.04.27 |
2. 스레드 및 스레딩 (0) | 2009.04.27 |