리액티브 프로그래밍(1) - 동기/블로킹, 스레드풀

HodaeSsi·2024년 10월 13일
0
post-thumbnail

리액티브 프로그래밍비동기-논블로킹 구조를 채택하여 기존의 동기-블로킹 구조의 시스템보다 같은 수의 스레드로도 더 높은 동시성을 달성할 수 있는 프로그래밍 방식입니다.
리액티브 프로그래밍은 기존의 동기-블로킹 방식과는 많이 다른 구조로, 많은 다른 개념들을 내포하고 있기 때문에 기존의 동기-블로킹 방식의 특징과 한계를 우선 정리해보고자 합니다. 특히, Spring Web MVC 프레임워크에도 적용되어 있는, 동기-블로킹 방식에서 병렬성을 달성하기 위한 대표적인 방법인 스레드 풀 구조에 대해서 알아보고자 합니다.

1. 동기-블로킹 특성

동기(Synchronous)의 의미

  • 작업이 순차적으로 실행됩니다.
  • 이전 작업이 완료되어야 다음 작업이 시작됩니다.

블로킹(Blocking)의 의미

  • 특정 작업이 완료될 때까지 스레드가 대기 상태에 놓입니다.
  • I/O 작업이나 네트워크 호출에서 자주 발생합니다.

대표적인 동기-블로킹 작업으로는 데이터베이스 호출이 있습니다.

public void processRequest() {
    String data = readFromDatabase(); // 데이터베이스 읽기 (동기-블로킹)
    String result = processData(data); // 데이터 처리
    sendResponse(result); // 응답 전송
}

위 코드에서는 readFromDatabase()가 완료될 때까지 스레드가 대기 상태로 점유되고, 다음 단계로 진행되지 않습니다.

위 코드와 같은 동기-블로킹 구조는 코드가 직관적이며 이해하기 쉽다는 장점이 있지만, 스레드가 대기 상태로 점유되어 CPU 자원의 사용이 비효율 적입니다.

2. 스레드 풀 구조

동기-블로킹 구조에서 매 요청마다 새로운 스레드를 생성하여 할당하면 스레드 생성비용이 반복적으로 발생하고, 무엇보다 수 많은 스레드 개수로부터 교착상태가 발생할 수 있습니다.
이 같은 문제점을 방지하기 위해 동기-블로킹 구조에서 안정적으로 병렬 프로그래밍을 구현할 때 대표적으로 스레드 풀 구조를 적용합니다.

스레드풀이란?

  • 미리 생성된 스레드의 풀(pool)을 활용하여 작업을 처리합니다.
  • 새로운 작업이 들어오면 빈 스레드가 작업을 실행하고, 완료 후 다시 풀에 반환됩니다.

작동 원리
1. 작업 큐에 작업이 추가됩니다.
2. 스레드풀이 큐에서 작업을 가져옵니다.
3. 스레드가 작업을 실행합니다.
4. 작업이 완료되면 스레드는 풀로 돌아갑니다.

// 최대 10개의 스레드로 동시에 요청을 처리할 수 있습니다.
ExecutorService executor = Executors.newFixedThreadPool(10);

public void handleRequests() {
    while (true) {
        Request request = acceptRequest(); // 새로운 요청 수신
        executor.submit(() -> processRequest(request)); // 스레드풀에 작업 제출
    }
}

스레드 생성비용을 줄이고 제한된 스레드 개수를 통해 안정적인 병렬 프로그램을 구현하였지만, 여전히 스레드 풀에는 대기 상태로 점유중인 스레드가 존재할 수 있어 CPU 자원 사용에 비효율적인 부분이 존재합니다. 또한, 지정된 작업 큐의 크기를 벗어난 작업은 등록할 수 없습니다.

결론

동기-블로킹 프로그래밍은 이해하고 구현하기는 쉽지만, 효율적인 CPU 리소스 사용에는 한계가 있습니다. 스레드풀을 활용하여 병렬성을 달성할 수 있지만 여전히 비효율적인 부분이 남아있습니다. 다음 포스팅에서는 이러한 한계를 극복하기 위한 비동기-논블로킹 프로그래밍에 대해 알아보겠습니다.

profile
항상 다같이, 즐겁게 일할 수 있으면 좋겠습니다 😎

0개의 댓글