멀티스레드 정책

최적화된 프로그램이란, 유휴 시간없이 하고 싶은 일을 최대한 많이 하는 프로그램을 의미합니다. 여기서 중요한 것은, 하고자 하는 일을 많이 해야 된다는 점이죠.  

싱글 스레드 클라이언트 프로그램의 경우는 대게 아래와 같습니다. 

  1. 입력 받는 작업
  2. 연산 작업
  3. 화면 그리기 
  4. 1번으로 돌아감

시간을 재고, 특정 작업 시간이 오래 걸려 재 속도를 내지 못한다면, 연산량을 감소 시킬 수 있는 처리를 하거나 (초당 프레임 조정 등), 만약 연산량을 줄일 수 없는 경우라면 게임 속도가 느려지게 됩니다.   연산량을 감소시켜서라도 제속도를 낼 수 있는 임계치를 최소 사양이라고 부릅니다.   멀티 스레드 서버 프로그램의 경우는 어떨까요?

처리 스레드 종류에 대한 가정

  • 소켓 이벤트 처리 6개 스레드
  • 패킷 처리 1개 스레드
  • 디비 처리 6개 스레드
  • 디비 처리 결과 반환 1개 스레드
  • 주기적인 로직 1개 스레드

1. 모든 스레드를 자유롭게 동작하도록 풀어놓고 스레드끼리 중복된 데이터를 사용할 일이 있을 때, 동기화 객체를 사용해서 관리해주는 방식(이하스레드 데이터 동기화 방식)

네 좋습니다. 그런데, 만약 상호 데이터 교환이 많은 경우는 어떨까요? A라는 데이터를 모든 스레드에서 요구한다면? A라는 데이터 사용중에는 다른 모든 스레드가 멈춰있겠죠? 싱글 스레드와 다를 것이 없게 됩니다.  물론 데이터가 겹치는 상황이 적다면 안정적인 속도로, 안정적으로 돌아가겠지만 글쎄요. 지금 당장은 그렇겠지만 기능이 추가되면서 분명히 지속적으로 성능 저하를 가져올 겁니다.

  2. 로직은 한 스레드에서만 돌리고, 비동기로 이루어져도 되는 처리에 대해서 요청한 후, 그 처리가 끝난 후에 신호를 받아 다시 처리하는 방식 (이하 원스레드 메시지 프로그래밍 방식)

이 방식은 Win32에서의 메시지 프로그래밍 방식과 매우 흡사한 방식이죠.   이렇게 했을 경우, 이벤트간 순서 제어도 직관 적이고, 데드락 위험성도 없으며, 비동기 스레드가 몇개 되지 않고 오래 걸리지 않는다면 처리 속도도 좋습니다.  문제는 싱글 스레드 이상의 효율을 내지 못한다는 것입니다. 로직 스레드에서 해야 될 일이 많다면…? 로직 스레드도 요청을 큐에서 꺼내서 처리하는데, 이 큐에 쌓이는 속도가, 데이터를 처리하는 속도보다 오래 걸린다면 전체적인 처리속도가 계속 늦어져 결국엔 사실상 아무일도 못하는 상태가 될 겁니다.   그래서 결국 로직을 멀티스레드로 분리하는 작업이 필요해집니다.   3. 로직을 멀티스레드로 처리하고 스레드끼리 중복된 데이터를 사용할 일이 있을 때, 동기화 객체를 사용해서 관리하고, 비동기 작업은 별도 스레드에서 처리하는 방식(이하 멀티 스레드 데이터 동기화 방식)

이렇게 했을 때의 맹점은, 첫번째 방식과 같습니다. 로직 스레드끼리 겹치는 데이터가 많을땐 느려지죠.   그래서 로직을 멀티스레드로 하면서 겹치지 않도록 해야 합니다.   4. 로직을 멀티스레드로 처리하고, 이 상황에서 스레드끼리 겹칠만한 일들과 비동기 처리를 별도 스레드에 맡기고, 로직 스레드에서는 다른 스레드와 겹치지 않는 일을 함으로써 Lock-Free 한 상태로 스레드를 관리(이하 멀티 스레드 메시지 프로그래밍 방식)   두번째 방식이랑 비슷하죠? 비슷하지만 다른 점은, 비동기로 해야 될 일 뿐만 아니라, 스레드끼리 겹칠만한 동작 자체도 별도 스레드로 맡긴다는 점입니다. 그리고 로직 스레드에서는 주의 깊게 (자신에게 주어진 데이터에만 접근하도록) 코딩 하는 것이 중요합니다. 상호 스레드간에 데이터 교환이 메시지 방식으로 이루어지도록 기반이 잘 갖추어져 있다면 위에서 설명한 세가지 방식보다 우월한 처리 효율을 보여줄 수 있습니다.   여기서 원스레드던, 멀티스레드던 메시지 프로그래밍 방식을 취했을 때 주의점이 있습니다. 데이터가 쌓이는 속도보다 푸는 속도가 빨라야 지연이 발생하지 않습니다. 만약 푸는 속도가 더 느리다면 스레드를 증가 시켜야 하는데, 여기서 주의 사항이 생깁니다. 스레드간에 데이터 교환을 위한 락이 적게 걸려야만, 스레드 갯수를 증가시켜서 얻는 잇점이 커지는 것이죠.  

스레드간에 데이터가 쌓이는 속도 대비, 풀리는 속도 측정이 되야 이 데이터 처처리 흐름에 문제가 여부를 알 수 있습니다. 물론 쌓이는 속도와, 풀리는 속도의 효율이 좋다 하더라도, 데이터가 모든 처리되기까지의 시간이 오래 걸린다면 그것도 효율이 좋다고 할순 없습니다.

지금까지 일반적으로 많이 사용되고 있는 스레드 사용 방식의 장단점에 대해서 알아보았습니다. 더 좋은 방법에 대한 논의는 지금도 계속 이루어지고 있고, 의견이 분분하지만, 제 의견과 생각에 대해서 정리를 해보고 많은 분들의 의견을 듣고 싶어 글을 올려봅니다. 의견 있으시면 언제든 댓글이나 메일 주세요. 감사합니다.

Comments

comments powered by Disqus