그리고 그 워커 스레드들에게서 하나의 큐를 통해 Job을 전달받는 메시지 처리 스레드가 존재했었습니다.
이 구조에서는 메시지 처리 스레드는 메시지가 없으면 큐가 계속 비었는지 있는지 확인하는 방식의 폴링을 채택했었습니다.
물론 이벤트를 이용해서 폴링을 하지 않고 잠들게 할 수 있었지만, 그렇게 하지 않고 그냥 폴링을 통해 하나의 cpu를 계속 사용하도록 만들었었습니다.
이부분에서는 장점도 있고 당연히 단점도 있을겁니다.
우선 장점으로는 구현이 매우 쉬워질겁니다.
그냥 무작정 폴링만 하면 되는 구조다보니 따로 이벤트를 사용할 필요도 없고 이벤트의 특성상 signal과 non signal상태만 존재하다 보니 연속으로 3건이 들어왔을때, 이 상황에대한 추가적인 해결방안이 들어가는등의 로직이 요구되지만, 그부분을 없앨 수 있었기 때문에 코드가 심플해진다는 장점이 있습니다.
단점으로는 하나의 스레드를 무한정 폴링하는 구조이기 때문에 작업이 없는 상황임에도 불구하고 퀀텀타임을 모조리 사용해버릴거고 하나의 cpu를 물고있는 상황이기 때문에 워커 스레드와의 경합을 피하기위해서는 코어를 모조리 사용하지 않게 가용 코어 - 1개의 상태를 유지해야 하는 단점도 존재했습니다.
하지만 더욱 근본적인 불만점은 이게 아닙니다.
과연 채팅서버에 업데이트 스레드라는 추가적인 요소 자체가 들어갈 필요가 있는지 우선 고민을 해봐야 할 것 같습니다.
당연히 채팅서버는 스스로 계산해서 로직을 만들어내는 경우는 거의 없을겁니다.
거의 99%이상이 IO를 통한 로직 처리일겁니다.(사실 100%라고 봐도 거의 무방할 정도입니다)
IO를 통해서만 Send할 것이 생기는 스레드 특성상 굳이 IO를 하고 그걸 큐잉을하고 그걸 뽑아내서 다른 스레드가 작업을 하기위한 폴링을 한다는 것 자체가 이미 낭비가 일어나고 있는것일수 있습니다.
이 상황에서 워커 스레드가 그대로 작업을 처리하는 구조가 된다면 불필요한 폴링없이 필요한 순간에만 작동하게 되므로 더욱 유연하게 작동하지 않을까 라는 점을 고민해봤습니다.
당연히 워커스레드가 멀티스레드로 돌기때문에 컨텐츠에서의 Lock은 불가피하게 되었지만... 모든 것을 락걸겠다는 것은 아니고 당연히 필요한 순간에 섹터 단위로의 락을 걸게 될겁니다.
추가로 SRWLOCK의 장점인 Shared, Exclusive를 이용한 형태로 갈것이기 때문에 검색만을 요구할때는 동시에 모든 스레드가 진입가능할것입니다.
만약 이동요청이나 로그인, 로그아웃 등과 같은 섹터나 플레이어 컨테이너의 요소를 수정하는 작업을 처리한다면 그때만 섹터에 Exclusive로 락을 걸어서 단일 스레드의 진입을 확보하면 충분히 해결 가능한 상황일것입니다.
그리고 다음과 같은 구조는 섹터의 갯수가 늘어나면 늘어날수록 경합의 확률이 획기적으로 줄어드는 장점을 지니고 있습니다.
제 채팅 서버의 경우에는 단일 월드로 섹터는 2500개 정도가 되기 때문에 실제 워커 스레드들끼리 경합이 일어날 확률은 현저히 낮습니다.
하지만 처리하지 않은 문제가 여전히 하나 남아있습니다.
Critical Section의 최대 장점이라고 할수있는 Recursion Lock이 SRWLOCK에서는 지원되지 않는다는 점입니다.
사실 이게 무슨 문제냐라고 하실수 있습니다만 Lock을 건 스레드가 또 Lock을 걸게되는 경우가 지금 네트워크 라이브러리에서 발생하고 있었습니다.