본문 바로가기
📕 Spring Framework/Spring 개념 정리

[Reactive Programming] 비동기-논블로킹 프로그래밍

by GroovyArea 2023. 2. 11.

동기 프로그래밍 (Synchronous)

  • 작업의 실행 흐름은 순차적으로 동작
  • 코드를 파악하기 쉽고 결과를 쉽게 예측 가능하므로 디버깅이 쉬움
  • 특정 작업 중 다른 작업을 할 수 없다는 단점

 

비동기 프로그래밍 (Asynchronous)

  • 작업의 실행 흐름은 기본적으로 순차적이지 않음
  • 현재 실행 중인 작업 이외에 다른 작업 가능.
  • 클라이언트, 서버 등 모든 환경에서 유용하게 사용 가능
  • 대표적으로 CallBack, Promise, Future, Coroutine 등이 있다.

 

비동기 프로그래밍 구현  방식 - Kotlin

 

Thread

  • 가장 기본이 되는 방식
  • Runnable 인터페이스를 구현하여 구현
  • 하나의 스레드 - 싱글 스레드, 다중 스레드 - 멀티스레드
fun main() {
    for (i in 0..5) {
        val thread = Thread{
            println("current-thread-name : ${Thread.currentThread().name}")
}
        thread.start()
    }
}

// 실행 시 결과가 매번 다름
  • 스케줄링 알고리즘에 의해 스레드가 전환되는 작업을 컨텍스트 스위칭이라 한다.
  • OOM 이 터질 우려가 있다. 응답 지연이 발생할 가능성이 있다.
  • Thread Pool 을 생성하여 안전하게 해결 가능
fun main() {
    val pool: ExecutorService = Executors.newFixedThreadPool(5)
    try {
        for (i in 0..5) {
                    pool.execute {
                        println("current-thread-name : ${Thread.currentThread().name}")
                    }
        }
    } finally {
            pool.shutdown()
    }
    
    println("current-thread-name : ${Thread.currentThread().name}")
}

=> 스레드 풀을 생성한 모습

 

Future

  • 비동기 작업에 대한 결과를 얻고 싶은 경우 사용
  • 다른 작업을 병행하며 수행할 경우 유용
  • Runnable 말고 Callable 을 사용하여 결과를 얻을 수 있다.
  • get 함수는 무한정 대기, 타임 아웃까지 블로킹 된다는 단점
  • 동시에 실행 되는 다중 비동기 작업에 대한 결과를 하나로 조합하여 처리하지 못함.
fun sum(a: Int, b: Int) = a + b

fun main() {
    val pool = Executors.newSingleThreadExecutor()
    val future = pool.submit(Callable {
    	sum(100, 200)
	})
 
    println(" 작업 시작 ")

    val futureResult = future.get() // . println(futureResult)
    println(" 작업 완료 ")
}

 

 

Completable Future

  • Future의 단점을 극복하기 위해 JDK 8 부터 나옴
  • get을 사용할 경우 블로킹이 된다.
fun main() {
    val completableFuture = CompletableFuture.supplyAsync {
        Thread.sleep(2000)
        sum(100, 200)
	}
    
    println(" 작업 시작 ")
    //completableFuture.thenApplyAsync(::println)
    
    while (!completableFuture.isDone) {
        Thread.sleep(500)
        println(" 작업 결과 집계 중.") 
	}

    println(" 작업 종료. ")
}

// thenApplyAsync를 통해 논블로킹 동작, 별도의 스레드 풀 지정 가능
// isDone은 작업 완료인지 체크
// isCancelled 를 통해 취소 상태 체크, 작업 도중 에러 발생 상태를 체크하는 isCompletedExceptionally

=> 대다수의 비동기 처리에서 유용하게 사용 가능하다.

=> 대다수의 API를 호출하고 결과를 결합 후 응답하는 시나리오에서 매우 유용함!

반응형