job 방식을 사용하기 전에는 모든 함수에 락을 걸어놓는 원시적인 방법으로 구현했었다.
작은 규모의 프로젝트면 큰 문제가 없지만 큰 규모의 프로젝트라면 스레드간의 경합이 심해지게 되면서 큰 문제가 발생한다.
위와 같이 구현했을 때, 만약 다수의 클라이언트가 우연히 같은 함수를 처리하려고 시도하게되면,
먼저 락을 잡고 처리중인 하나의 스레드가 일을 마칠동안 다른 스레드는 대기하면서 자원만 낭비할 것이고,
심지어 스핀락으로 구현했기 때문에 계속 루프를 돌며 cpu 자원만 고갈하게 될 것이다.
이를 해결하기 위해 커맨드 패턴을 사용한 Job방식을 사용했다.
어떤 요청을 객체로 담고 있다가 건네주는 것이 핵심.
이렇게 해주면 락을 잡으려고 모든 클라이언트가 같은 함수에서 경합하는 것이 아니라 순차적으로 일감(Job/Task)을 만들어서 일감만 넘긴 다음 빠져나와서 자기가 할 일을 하게 될 것이다.
job 방식이 스레드 경합을 어떻게 줄이는가?
어떤 플레이어가 광역으로 스킬을 뿌리는 C_Skill 패킷을 서버에 요청했다고 가정해보자.
Job방식이 있다면, 이를 하나의 일감으로 만들어서 JobQueue에 바로 밀어넣고 JobQueue에 들어간 일감들이 순서대로 실행된다.(번호표처럼)
Job이 없다면, C_Skill_Handler 쪽에서 플레이어가 있는 공간에 접근한 다음 lock을 잡은 상태에서 스킬 로직을 쭉 실행해야 한다.
스킬 로직이 1초 정도 걸리는 무거운 연산 작업이라면, 나머지 쓰레드들은 1초 동안 대기를 하면서 lock이 풀릴 때까지 기다려야 한다.
결국 멀티쓰레드임에도 병목현상이 심해지기 때문에 cpu를 효율적으로 활용할 수 없게 된다.
즉, 쓰레드가 일감을 끝까지 다처리하는 것이 아니라 일감을 밀어넣고 다시 자기의 일로 돌아온다.
그리고 밀어넣은 일감은 job을 처리하는 쓰레드에서 꺼내서 처리해준다.
Job방식에서는 락을 사용하지 않는다.
그럼에도 멀티쓰레드 환경에서도 동작하는 이유는 직접적으로 접근해서 호출하는 것이 아니라,
Job을 통해서만 접근하여 호출할 수 있다. (애초에 접근할 때 스레드 세이프하게 접근해서 Job을 가져와서 호출함)
이런 식으로 멀티쓰레드(스레드 경합)에서의 병목을 줄인다.
'기술 면접' 카테고리의 다른 글
db 클러스터드 인덱스(Clustered Index) (5) | 2024.07.20 |
---|---|
Protobuf (1) | 2024.07.20 |
관계형 DB(SQL)와 비관계형DB(NoSQL)의 차이 (0) | 2024.07.19 |
SP 장단점(Stored Procedure - 저장 프로시저) (0) | 2024.07.19 |
iocp (0) | 2024.07.15 |