동기와 비동기, 블로킹과 논블로킹 개념 정리
'동기와 비동기, 블로킹과 논블로킹 - 비슷한 듯하면서도 비슷하지 않은 개념들'
프로그래밍을 하며 자주 접하게 되는 단어들이지만 개념적으로 명확하게 차이를 짚고 넘어간 적이 없었는데 이번에 WebClient를 사용하기 위해 공부하던 중 Spring WebFlux, Reactive Programming의 개념을 접하며 논블로킹(Non-Blocking)에 대해서 그리고 블로킹과 동기, 비동기에 대해서도 정리할 필요성을 느껴 정리하게 되었습니다.
동기(Synchronous)와 비동기(Asynchronous)
- 동기 방식
동기(Synchronous) 방식은 데이터의 요청과 결과가 한 자리에서 동시에 일어나며, 현재 작업의 응답이 끝남과 동시에 다음 작업이 요청됩니다.
- 세탁기 돌리기
- 로봇 청소기로 바닥 청소하기
- 식기세척기 돌리기
예를 들어 위와 같은 청소 순서가 있을 때, 청소하는 사람이 세탁기를 작동시키고 세탁기의 세탁이 끝날 때까지 그 자리에서 기다립니다.
세탁기의 동작이 모두 끝나고 세탁이 완료되면 (응답이 끝남과 동시에 다음 작업) 로봇 청소기를 동작시켜 바닥을 청소합니다. 이때도 마찬가지로 로봇 청소기가 바닥청소가 끝날 때까지 그 자리에서 기다립니다. 로봇 청소기의 바닥 청소가 끝나면 마지막으로 식기세척기를 돌립니다. 역시 식기세척기 동작이 끝날 때까지 그 자리에서 기다립니다.
이것이 동기 방식입니다. 이러한 동기 방식은 설계가 간단하고 직관적이지만 결과를 받을 때까지 다른 작업을 못하고 대기해야 하는 단점이 있습니다.
(일반적으로 사용하는 함수들은 대부분 동기 방식으로 구현)
- 비동기 방식
비동기(Asynchronous) 방식은 두 주체가 서로의 시작이나 종료시간에 관계없이 별도의 시작, 종료 시간을 가지고 있다는 특징이 있으며, 동기방식보다 복잡하지만 결과를 받을 때까지 시간이 걸리더라도 그 시간 동안 다른 작업을 할 수 있기 때문에 자원을 효율적으로 사용할 수 있다는 장점이 있습니다.
(함수를 호출하는 곳에서 결과를 기다리지 않고, 다른 함수(Callback)에서 결과를 처리합니다.)
***
동기와 비동기는 어떠한 프로세스를 처리하는데 절차적인 측면이 강합니다.
이 둘의 차이점은 '호출되는 함수의 작업 완료 여부를 누가 신경 쓰는지'에 대한 관점으로 볼 수 있습니다.
블로킹(Blocking)과 논블로킹(Non-Blocking)
블로킹, 논블로킹은 주로 멀티 스레딩, I/O 등에서 사용되는 개념으로 함수의 리턴 시점과 제어권에 따라 차이가 납니다.
***
Blocking / Non-Blocking의 차이는 '제어권이 어디 있는지'에 대한 관점으로 볼 수 있습니다.
(제어권이란, 자신(함수)의 코드를 실행할 권리 같은 것으로 제어권을 가진 함수는 자신의 코드를 끝까지 실행한 후에 자신을 호출한 함수에게 제어권을 돌려줍니다.)
- 블로킹(Blocking)
호출된 함수로 제어권이 넘어가고, 호출된 함수의 작업이 끝난 후 결괏값이 호출한 함수에게 리턴됨과 동시에 제어권도 다시 호출한 함수에게 넘어오는 것을 이야기합니다.
- 논블로킹(Non-Blocking)
블로킹과 마찬가지로 호출할 때 제어권을 넘겨주기는 하지만 바로 돌려받습니다. 제어권을 바로 돌려받아서 가지고 있기 때문에 계속해서 다른 작업을 할 수 있습니다.
(위에서 사용한 예시)
- 세탁기 돌리기
- 로봇 청소기로 바닥 청소하기
- 식기세척기 돌리기
- 동기(Synchronous) / 블로킹(Blocking)
위 예시를 사용하여 청소하는 사람이 세탁기를 돌렸을 때,
동기 방식이기 때문에 호출되는 작업의 완료 여부를 호출하는 함수에서 확인해야 합니다. 즉, 세탁기가 돌아간 결과를 청소하는 사람이 확인해야 하고, 블로킹으로 인해 세탁기를 돌렸을 때 제어권은 세탁기로 넘어갑니다.
세탁기가 동작을 마쳤을 때 '세탁기가 돌아간 결과(호출된 함수의 결과)'와 '제어권'이 청소하는 사람에게 넘어와서 진행됩니다.
- 동기(Synchronous) / 논블로킹(Non-Blocking)
논블로킹이기 때문에 호출되는 함수는 제어권을 바로 호출하는 함수에게 다시 넘겨줍니다. 동기 방식이기 때문에 호출하는 함수 쪽에서 작업 완료 여부를 신경 써야 합니다. 때문에 호출된 함수가 끝났는지 확인하는 요청이 필요합니다.
예시로 보면 청소하는 사람은 세탁기를 작동합니다. 제어권은 바로 청소하는 사람한테 다시 넘어오기 때문에 청소하는 사람은 '다른 업무(로봇 청소기로 바닥 청소하기)'를 할 수 있습니다. 하지만 동기방식이기 때문에 세탁기가 끝났는지 수시로 확인하고, 끝났을 경우 '결괏값을 받아와야 하는 일'이 남아있습니다.
- 비동기(Asynchronous) / 논블로킹(Non-Blocking)
가장 일반적으로 이야기되는 비동기 처리 방식입니다. 비동기 방식이기 때문에 함수를 호출한 곳에서 결과를 처리하지 않고 다른 곳에서 결과를 처리합니다.(Callback)
또한 논블로킹으로 인해 함수가 호출되고 제어권은 바로 호출한 함수로 다시 넘어오게 되어 다른 작업을 이어서 진행할 수 있게 됩니다.
청소하는 사람은 '세탁기를 작동해놓고' '결과를 신경 쓰지 않은 상태'로 바로 다음 업무인 로봇 청소기로 바닥 청소하기를 위해 로봇 청소기를 가동합니다. 마찬가지로 '로봇 청소기의 청소 결과를 신경 쓰지 않은 상태'로 다음 업무인 식기세척기를 가동합니다.
세탁기와 로봇 청소기, 식기세척기의 결과는 '다른 청소하는 사람(Callback 함수)이 완료된 소리를 듣고 처리'합니다.
- 비동기(Asynchronous) / 블로킹(Blocking)
비동기 방식이기 때문에 호출하는 함수는 작업 완료 여부를 신경 쓰지 않지만, 블로킹으로 인해 함수 호출 순간 호출되는 함수에게 제어권이 넘어가고, 호출하는 함수는 제어권을 다시 받을 때까지(호출된 함수가 끝날 때까지) 다른 작업을 할 수 없습니다.
예를 들면 청소하는 사람이 세탁기를 동작시켰습니다. 청소하는 사람은 세탁기의 결과를 신경 쓰지 않아도 되지만 세탁기의 문제로 세탁기의 세탁 작업이 끝날 때까지 다른 일을 할 수 없습니다.
< 참고 자료 >