(Gemini 2.5 Pro)

멀티스레드 방식 vs 액터 모델

항목 C++/IOCP 멀티스레드 액터 모델
핵심 철학 상태 공유 (Shared State) 상태 분리 (Isolated State)
동기화 락 (Lock) 사용 락 없음 (Lock-free)
성능 병목점 락 경합 (Lock Contention) 메시지 큐 처리량
코어 활용률 코어가 많아도 경합으로 인해 성능 한계 명확 코어 수에 비례하여 선형적 성능 증가
수평 확장 매우 어려움. 별도의 거대한 아키텍처 설계 및 구현 필요 매우 쉬움. 프레임워크가 대부분 지원
안정성 하나의 버그가 서버 전체를 다운시킬 수 있음 액터단위 격리로 시스템 전체 안정성이 매우 높음

동시성 차이

특징 전통적인 스레드 모델 Akka.NET 액터 모델
데이터 접근 여러 스레드가 공유 객체(userA, userB)에 직접 접근 각 액터의 내부는 오직 하나의 스레드만 메시지 처리 중에 접근
동시성 제어 개발자가 직접 lockmutex 등을 사용하여 데이터 보호 디스패처가 스케줄링을 통해 동시 접근을 원천 차단 (락 불필요)
**스레드와
객체 관계** 스레드가 객체의 메소드를 직접 호출 스레드는 액터에게 임대되어 메시지 하나만 처리하고 반납됨
상호작용 메소드 직접 호출 (동기적) 메시지 전달 (비동기적)

만약 A가 B를 공격했을 경우 A의 처리가 끝나고 B를 처리하러 가기 때문에 순서는 걱정하지 않아도 된다고 한다.

간단한 동시성 처리 예제코드

간단한 디스패처 동작

디스패처와 스레드풀

액터모델 내부에는 디스패처라 불리는 스케쥴러가 존재하며, 이 디스패처가 스레드풀을 관리하고 액터를 실행하는 모든 과정을 책임진다.

이 스레드들은 액터 시스템이 종료될때까지 계속 재사용된다. Akka.NET에서는 이 스레드 풀을 Executor(집행자)라고 부르며, 가장 흔하게 사용되는것은 ForkJoinExecutor. 이는 작업 훔치기 기능을 포함하여 스레드를 최대한 쉬지않고 일하게 만드는데 최적화 되어있음.

액터 스케쥴링

액터는 메시지가 있을때만 수행이 된다. 어떤 액터의 메일박스에 메시지가 들어오면 디스패처에게 신호를 보낸다. (이 신호는 이벤트 방식)

  1. 디스패처는 스레드를 액터에게 빌려주는 방식으로 동작한다. 디스패처 일감에 있는 액터를 하나 선택하고, 스레드 풀에서 놀고있는 스레드를 가져와 Actor에 할당.

  2. 일괄처리(batch Processing)는 Actor의 메시지를 하나만 처리하고 바로 반납하면 매우 비효율 적이기 때문에 바로 반납되지 않는다.

대신 Thread1은 정해진 개수(=이 설정을 스루풋이라고 함)만큼의 메시지를 연속으로 처리한다. (마치 GQCSEx처럼 처리되는 듯). 모든 처리를 완료하면 반납된다.

  1. 단일 스레드 보장 : Thread1이 ActorA를 처리하는 동안 디스패처는 절대로 다른스레드를 ActorA에게 할당하지 않는다. 이것이 바로 액터 내부의 코드가 항상 단일 스레드에서 실행됨을 보장하는 핵심

  2. 메시지 처리가 끝나면 즉시 스레드풀로 반납된다.

  3. 다음작업으로 반납된 Thread1은 다른 액터를 처리하러 갈 수 있음.