출처 : http://www-06.ibm.com/jp/developerworks/linux/021101/j_l-sc5.html

번역 : 2007. 06. 28 최흥배 ( jacking75@gmail.com )

 

 

서버 클리닉 : 어른을 위한 병행성

스레드 화만이 아니다

 

Cameron Laird (claird@phaseit.net),  Phaseit, Inc. , 부사장

 

 

20028 원문은 이쪽(US)

병행성(concurrency) -- 다중처리 (multi-processing) -- 대해서 많은 사람들은 잘못한 해석을 하고 있습니다. 이번 달의서버 클리닉에서는 서버로의 일을 안전하게 해내 가기 위해 필요한 병행성의 기본적인 생각을 소개합니다.

다중처리라고 하는 이름의 아래에서 많은 오해가 생기고 있습니다. 대부분의 대학 과정에 있어서 수많은 프로그래밍의 교과서가 병행성의 개념을 명확하게 설명하고 있습니다만, 이것은 어려운 테마이며 우리의 대부분이 재교육 강습을 받아도 괜찮을 정도 입니다.

병행성(Concurrency)이란, 동시에 복수의어플리케이션 실행하고 있는 상태를 가리킵니다. 여기서어플리케이션이라고 하는 말을 괄호 첨부로 사용한 것은 병행성의 의미가 상황에 의존하기 때문입니다. Linux 호스트는 항상 프로세스 테이블을 수에 차이는 있다고 해도 동시에 실행되는 1군의 프로그램으로 묻습니다. 네트워크 프로토콜 demon, cron 매니저, 커넬 자신 등입니다. 그리고, 많은 경우 많은 프로그램이 이것에 참가합니다. Linux multitasking 오퍼레이팅 시스템이며 이러한 기능을 하도록 설계되고 있습니다.

일반적인 단일 프로세서 호스트에서는 복수의 태스크가 정말로 동시에 실행 되는 것은 없습니다. 커넬 중의 스케쥴러로 불리는 부분이 모든 작업에 차례가 돌도록 작업의 변환을 실시합니다. 여러분이 프로그램의 소스를 편집하거나 음악을 재생시키고 있는 동안 브라우저는 다운로드를 실시합니다. 병행성은 외관상의 동시성에 많이 관계하고 있습니다.


병행성의 2개의 측면

병행성에 대한 유저로부터의 시점과 프로그래밍 모델이라고 하는 것은 어느 쪽이나 단일 자원에 대한 액세스의 스케줄링의 문제인 것을 명심해 주세요. 그렇지만 이것을 보완하는 병행성에 대한  2번째의 뒤쪽에서 의미가 있습니다. 프로세서 자체의 성능을 추구하고 있는 사람들은 이쪽 다른 측면을 강조합니다. 그들에게 있어서는다중처리라고 하는 것이 의미하는 것은 일반적으로 단일의 태스크를 복수의 부분에 분할 하여 복수의 중앙 처리장치(CPU) 공동 처리할 있도록 하는 것입니다. 이것은 하드웨어가 여분으로 필요하거나 프로그래밍이 복잡한 것이 되었다고 해도  external clock 측정했을 때에 하나의 작업을 보다 짧은 시간에 완료하고 싶다고 하는 생각입니다.

병행성의 2개의 측면은 모두 스케줄링, CPU로의 태스크의 할당과 관계하는 것입니다. , 양쪽 모두  usability 관계해 옵니다. 그런데  이것들 2개의 측면은 귀찮은 일로  세상에서 일반적으로 혼동 되고 있습니다. 초심자의 프로그래머는 병행성의 가장 중요한 수법의 하나인multi-thread (multi-threading) 라고 하는 수법으로 대해서 잘못된 생각을 안는 경향이 현저합니다. 스레드 화(threading) 라고, 자주 생략해지는 수법입니다만 오해에는 이하와 같은 것이 포함됩니다.

  • 스레드에 의해서 프로그램을 고속으로 실행할 있게 된다.
  • 스레드가 병행성의 유일한 실현 방법이다, 혹은 유일한 실용적인 방법이다.
  • N 웨이의 호스트는 단일 프로세싱의 호스트의 거의 N 배의 속도로 처리를 실시한다.

이러한 오해는 컨셉을 아주 조금 명확하게 하는 것만으로 즉시 시정되는 부류의 것입니다.

경험이 부족한 개발자는 자주 이렇게 말합니다. 나의 프로그램은 처리가 너무 늦다 . 빠르게 하기 위해 어떻게 하면 스레드 할 있는 것일까. 많은 경우 답은그것은 무리라고 하게 됩니다. 기존의 싱글 태스킹의 어플리케이션을 처리 방식을 재검토하지 않고 단지 multitasking 일부로 해도 오히려 계산 처리가 증가하는 것 뿐입니다. 일반적으로 그러한 프로그램을스레드하면 처리 시간은 길게 됩니다.

물론 이러한 오해가 횡행하고 있는 데는 이유가 있습니다. 많은 프로그램은 부품으로 분해하는 것으로 병목(bottlenecks) 해소를 도모할 있습니다. 스페이스셔틀의 재돌입 시뮬레이션과 같은 계산 중심의 작업은 하나의 CPU 아니고 8개의 CPU 분산 처리하면 처리 시간을 단축할 있겠지요. 일반적인 예로서는 입출력에 의한 처리의차단 일어나지 않게 프로그램을 재구성한다고 하는 것입니다. 일반 유저 레벨 (consumer-level) 어플리케이션으로 키보드 입력이나 디스크로부터의 데이터의 스왑 인이나 네트워크로부터의 메세지의 도착을 대기하는 동안 유익한 일을 실시할 있는 경우에는 아무것도 희생을 지불하는 없이 속도 향상할 있겠지요.

 

스레드의 한계

그러나, 이러한 속도 향상을 스레드에 의지하는 것은 위험합니다. 속도 향상에는 깊은 분석이 필수입니다. 속도 향상은 충분히는 이용되어 있지 않은 자원을 사용할 있게 되었을 때에만 가능합니다. 게다가 스레드만이 병행성을 실현하는 방법이라고 하는 것은 아니고 스레드가 최선의 방법이 아닌 것도 자주 있습니다.

학술 문헌에는 실제로 쓸모가 있다고 생각되는 병행성의 모델이 적어도 10개 이상 연구, 소개되고 있습니다. 스레드 외에도 프로그램적인 의미로의 다중처리, 콜 루틴, 이벤트 베이스 프로그래밍 아마 연속이나 제너레이터도 난해한 구조 여러 가지 들었던 적이 있는 것은 아닐까요. 예를 들어 제너레이터를 지원하는 스레드는 지원하고 있지 않는 언어가 있을 제너레이터를 사용해 스레드 에뮬레이션을 기술할 있다(혹은 역도 가능)라고 하는 의미로 이러한 수법은 모두 형식적으로 거의 등가입니다.

물론, 적절한 것을 프로그래밍 하는 것과 추상적인 의미로 등가인 것으로는 다릅니다. 신뢰성 높은 어플리케이션을 개발해 스케줄대로 납품하려는 경우 여러 가지 병행성 모델의 사이에는 실제적인 차이가 있습니다. 예를 들어 스레드에는 벌써 년의 사이 말해져 오고 있는 약점이 있습니다. 스레드의 경우 비교적 저 레벨의 프로그래밍 구성물입니다. 문제를 일으키지 않고 프로그램을 작성하는 것이 곤란합니다. 스레드를 조종하는 프로그램은 데이터의 부정 궁합, 데드 , 빠져 나갈 없는 록킹, 우선 순위의 역전에 빠지기 쉬운 성질이 있습니다.  Java 최근이 되어 스레드의 성능의 문제로부터 병행성의 개념의 핵으로서 multi-thread만을 지원 한다고 하는 초기의 사상을 버리고 있습니다. 스레드 대응의 디버거는 악평을 정도의 고가를 매길 있고 있습니다.

단지, 나쁜 뉴스만이 아닙니다. 시간을 들여 기본적인 생각을 명확하게 이해한다면 여러분이 XML 이나 LDAP 등의 전문 영역에서 실시하고 있는 작업과 같이 신뢰성의 높은 방법으로 스레드를 이용할 있습니다. 곧바로라고 한다면 안전한(경우에 따라서는, 고속의) 병행성 모델을 여러 가지 상황에 맞추어 이용할 있습니다.

수많은 상황으로 어플리케이션을 멀티태스킹 화하기 위한 최선의 방법은 어플리케이션을 스레드가 아니고 공동 처리를 하는 프로세스로 분해하는 것입니다. 프로그래머는 통상 사실에 저항합니다. 하나는 역사적인 이유로부터입니다. 프로세스는 스레드보다 아득하게무겁다 것이였고, 그것은 Windows 타입의 OS에서는 지금도 들어 맞읍니다. 그러나, 최근의 Linux에서는 다른 프로세스간의 문맥 스윗치는 그것과 동등한 동일 프로세스 내의 스레드간의 문맥 스윗치보다 15% 밖에 불필요하게 시간이 걸리지 않는 경우도 있습니다. 그러한 시간적인 코스트와 교환에 얻을 있는 것은 아득하게 이해하기 쉽고, 견고한 프로그래밍 모델입니다. 수많은 프로그래머가 독립한 프로세스를 문제를 생기게 하지 않도록 기술할 있습니다. 스레드를 채용했을 경우 문제를 일으키지 않는 것은 상대적으로 적게 됩니다.

그럼, multi-thread 아니고 멀티 프로세스로 하는 편이 좋은 것은, 어떤 경우일까요. 예를 들어, 컨트롤 패널이라고 하는 그래피컬 유저 인터페이스(GUI) 사용하여 몇 개의 대규모 계산의 결과를 모니터 하는 데이타베이스의 레코드의 읽기와 갱신을 실시하며 외부의 물리 디바이스에 관한 리포트를 표시하는 것으로 합니다. 이것들 전부를 하나의 프로세스로서 처리하여 개개의 태스크를 각각 별개의 스레드로 처리할 수도 있겠지요. Windows에서는 많은 경우 기꺼이 이런 방법이 취해집니다.

그러나, 내가 개발을 실시하는 경우 각각의 태스크를 각각 프로세스로 하고 소켓이나 파이프나 때로는 공유 메모리를 사용하여 서로 교신하도록 한다는 것이 통상의 방식입니다. 이렇게 하면 통상 사용하는 커멘드 라인 툴을 모두 사용할 있어 복수의 프로세스를 자동화할 있기 때문에 단체 테스트를 매우 간단화할 있습니다. 하나의 프로세스가 크래쉬 했다고 해도 다른 프로세스에 해를 미치는 일이 없습니다. 성능은 통상  multi-thread화와 거의 같고, 구체적인 하드웨어나 프로그래밍 나름입니다만  multi-thread화보다 높은 성능이 되기도 합니다.

 

다른 병행성 모델

이러한 멀티 프로세스의 구현은 자주 이벤트 베이스 프로그래밍에 의존이 큽니다. 이벤트라는 것은 I/O 관련하는 multitasking 처리를 관리하는데 적당한 병행성의 명확한 하나의 컨셉입니다. 이벤트는 비동기적인외부의 사건 (externalities) 프로그램화 콜백에 대응 연결됩니다( 시그널이라든지 바인딩 등이라고도 말하는 것입니다)  .GUI 컨트롤 패널의 예로 생각해 보면 Unix에서 이것을 프로그래밍 높은 성능의 것으로 완성하는 경우 시스템 콜select() 데이터의 도착을 검출했을 경우에게만 표시를 갱신하도록 하면 좋습니다. C 지향의 프로그래머는 자주 이벤트 베이스의 수법을 select 관점으로부터 파악하고 있습니다.

여러분은콜루틴이나제너레이터 교실에서 다루어지는 이색적인 수법이라고 생각하고 있을지도 모릅니다만, 이러한 메커니즘은 Modula Icon 등의 언어의 정의에 짜 넣어지고 있습니다. Multitasking 프로그래밍의 표현이 강력하게 되는 것과 동시에 이해하기 쉽고 따라서 문제를 일으키지 않기 때문입니다. 복잡한 성능 요건이 부과되어 있는 경우라든지 몇 백개의 서브 태스크를 사용하는 것으로 어플리케이션이 가장 모델화할 있는 경우 혹은, 특히 서버 룸에 상당한 수의 멀티 웨이의 호스트가 수용되고 있는 경우에는 여러 가지 병행성 모델에 대해 연구해야 합니다. 각각의 모델에 이상적인 어플리케이션이 있는 것을 있습니다. 그러한 모델의 몇 개인가가 여러분의 요구에 맞고 있을지도 모릅니다.

, Linux 에서 사용하고 싶은 모델이면 모두 아마 지원 되고 있는 것으로 생각합니다. 후출의 참고 문헌 일람에 다양한 병행성 모델의 구현이나 실험에의 참조처를 다른 참고 문헌과 함께 나타내 보여 둡니다.

 

멀티 웨이의 수수께끼

마지막으로 하나만 주의: 다중처리의 하드웨어( 많은 경우, 시메트릭 멀티 프로세싱 (SMP: symmetric multiprocessing) ) 에서 multitasking 소프트웨어가 유효하게 동작한다고 판단해서는  안됩니다. 특히 Linux 낡은 버전의 시대에 자주 전문 기술을 구사하여 SMP 머신으로 유효한 결과를 올리는 것이 시도 되었습니다. Linux 2.4 디폴트의 인스톨에서도 4개까지의 (때로는 많은) 프로세서를 개개의 프로세스에 할당한다고 하는 것을 꽤 실시하고 있습니다. 단지 단일의 프로세스중의 스레드가 하나의 프로세서로 보틀 넥이 되고 있는 한편으로 다른 프로세서에서는 대기 상태가 되어 있다고 했던 것이 일어날 있습니다. 다른 병행성 수법도 같은 문제를 일으키는 일이 있습니다.

이러한 자원의 헛됨을 피하는 것은 개개의 플랫폼의 구체적인 사양에 의합니다. Linux 2.4 일반적인 멀티 웨이의 하드웨어의 편성에서는 디폴트의커넬 스렛드( 참고 문헌Linux 스레드의 FAQ 참조) 스레드를 적절히 스케줄 준다,   스레드를 다른 CPU 사이에 공유하도록 준다고 생각하면 문제 없습니다. 최선의 시스템 관리 툴 등을 사용하여 스케줄링이 올바르게 일하고 있는지 어떤지를 확인하거나 실제의 스레드 스케줄링에 대해서 Linux 벤더나 유저스 그룹에 구체적인 질문을 실시하거나 주세요.

여러분의 프로그래밍이 많은 부분은 자연스럽게 논리적인 태스크에 분해되고 있는 것으로 생각합니다. 병행성의 기본적인 생각을 명확하게 이해하면 그것을 여러분의 요구 사양에 맞추고 적용할 있게 됩니다. 병행성은 겉의 측면과 뒤의 측면의 양면을 갖추고 있는 것을 잊지 말아 주세요.유저의 시점프로그래밍·모델 어플리케이션에 어떻게 임하는지를 좌우합니다.뒤의 측면 하드웨어에의 태스크의 할당을 관리합니다. 기능적인 요건과 성능 면에서의 요건이란 엄밀하게 구별할 필요가 있습니다. 마지막으로 병행성은 스레드 만이 아니다고 하는 것을 잊지 말아 주세요. 명시적으로 multi-thread화를 실시하지 않는 모델로 프로그래밍을 실시하여 서버를 최대한 활용할 있는 케이스도 많습니다.



참고 문헌

  • 나는 comp.programming.threads FAQ 에서 스타일에 대해 논쟁하고 있습니다. 라고는 말해도 이것이 귀중한 정보원인 것에 의문의 여지는 없습니다. 실제로 스레드 프로그래밍을 실시하지 않으면 되는 상황에 벌써 듬뿍 잠기고 있다면 더욱 그러합니다.
  • Linux Threads Home Page C/C++ 이외의 언어도 확실히 인식하고 있습니다. 문제는 년간 거의 갱신되어 있지 않다고 하는 점입니다. 라고는 말해도 유저 레벨 커넬 레벨의 multi-thread, 협조적인 스케줄링과 비 선점형인 스케줄링 등에 대해서 귀중한 논의를 게재하고 있습니다.
  • IBM Linux 테크놀로지 센터 멤버에게는 차세대형 POSIX 스레드 (Next Generation POSIX Threading) 프로젝트의 개발에 참가하고 있는 사람이 다수 있습니다. 이것에 대해서는 NGPT 페이지 상세가 나타나고 있습니다.
  • developerWorks 에는 여러 가지 시점이 소개되고 있습니다.
  • developerWorks Linux 존에는 밖에도 Linux 관계의 기사가 다수 게재되고 있습니다.

 

저자에 대해

Cameron Phaseit, Inc. 상근의 컨설턴트입니다. 오픈 소스 등의 기술적인 토픽에 대해서 수많은 집필이나 발언을 실시하고 있습니다. Cameron 메일 주소는claird@phaseit.net 입니다.


신고
by 흥배 2008.03.21 00:11
| 1 |

티스토리 툴바