실버바인 서버 엔진 2는 내부적으로 스레드 여러 개를 만들어서 DB 접근 등의 일을 처리하지만,
게임 로직 코드는 오직 메인 스레드에서만 돌립니다.
대신 파이버를 사용해서 여러 로직 코드가 동시에 (concurrently) 실행될 수 있도록 하고 있습니다.
이는 스레드 동기화 부담을 로직 프로그래머에게 지우지 않으면서도, I/O 대기로 인한 성능 저하가 일어나지 않도록 하는 설계입니다.
이런 설계에서는 게임 로직 코드가 동작하는 메인 스레드에서 blocking I/O를 실행하면,
I/O 결과가 돌아올 때까지 메인 스레드 전체가 멈추기 때문에, 심각한 성능 저하가 발생합니다.
그런데 .NET 내장 라이브러리는 물론이고, 수많은 서드파티 라이브러리들이 blocking I/O 모델을 채택하고 있기 때문에, 엔진이 제공하는 API 이외의 방법으로 I/O(주로 외부 시스템과의 통신이겠죠)를 할 때는 매우 주의해야 합니다.
실버바인 서버 엔진 2에서는 blocking I/O를 메인 스레드가 아니라 백그라운드 스레드에서 수행하고,
I/O가 끝났을 때 파이버를 통해 결과를 받을 수 있는 인터페이스를 제공하고 있습니다.
Func<T> work;
var result = await EngineAPI.ThreadPool.Execute<T>(work);
이렇게 하면 됩니다.
work는 .NET 프레임워크에서 제공하는 ThreadPool.ExecuteInThreadPool
을 통해 실행됩니다.
work가 정상적으로 수행을 마치면 그 결과가 await
식의 결과로 전달됩니다.
work 안에서 예외가 발생하면 그 예외가 Exception
으로 한번 감싸져서 호출한 파이버로 전달됩니다.
work는 다른 스레드에서 실행되므로, 메인 스레드와 공유하는 상태를 변경하지 않도록 주의하셔야 합니다.
EngineAPI.ThreadPool.FireAndForget(() => {})
이렇게 하면 됩니다.
반대로, 파이버와는 상관없이, 백그라운드 스레드에서 발생한 사건을 메인 스레드로 넘겨서 처리하고 싶은 경우에는 이렇게 하면 됩니다.
EngineAPI.ThreadPool.QueueToMainThread(() => {})
인자로 넘긴 Action
이 메인 스레드 안에서 실행되게 됩니다.
쉽죠?