[CS] Blocking/Non-blocking & Sync/Async 에 대해서
개요
안녕하세요. 이번 시간에는 Blocking/Non-blocking과 Sync/Async에 대해 알아보겠습니다.
이전에 공부하면서 이해하고 어느 정도 실무에 잘 반영하고 있다고 생각하고 있었는데 다시 공부하는 시점에 잘못 이해하고 있었던 부분과 시간에 지남에 따라 까먹은 부분들이 많아서 해당 주제를 다시 한번 학습하는 시간을 만들었습니다.
Blocking/Non-blocking & Sync/Async 개념과 예시 상황으로 커피 매장에서 손님이 직원에게 커피 주문하는 상황을 설명하겠습니다.
개념 및 사용법
메서드 & 함수를 호출하는 호출자(Caller)와 호출 대상(Callee)이 되는 메서드 두 가지 개념을 기억하고 해당 포스팅을 학습하시는 걸 추천드립니다.
Blocking
Blocking이라는 단어를 확인해 보면 다음과 같이 설명하고 있습니다.
상황에 따라 다양한 뜻이 존재합니다. 하지만 우리가 사용하는 상황에서 blocking이라는 뜻은 “블로킹(막다), 저지 현상”라는 뜻이라고 이해해야 합니다.
하나의 작업을 진행하고 있다가 해당 작업이 완료가 될 때까지 다른 작업을 수행할 수 없는 상황을 Blocking이라고 합니다.
즉, 호출 대상자(Callee)가 자신이 할 일을 모두 마칠 때까지 제어권을 계속 가지고 있어 호출자(Caller)에게 제어권을 바로 돌려주지 않는 방식을 말합니다.
결국 호출 대상자에 의해 호출자는 다음 로직을 수행할 수 없고 "차단" 된다고 볼 수 있습니다.
Non-blocking
그럼 Non-blocking은 무엇일까요?
Non-bloking은 호출 대상자(Callee)가 자신이 할 일을 끝내지 않아도 제어권을 호출자(Caller)에게 반환합니다.
그렇기 때문에 호출 대상자는 호출 메서드를 호출하고 나머지 나의 일을 할 수 있습니다.
Blocking을 이해했다면 반대되는 개념으로 이해를 하면 금방 이해를 할 수 있습니다.
Sync
Sync는 무엇일까요?
Sync는 단어 뜻 그대로 동시성, 동시 진행이라고 확인할 수 있습니다. 여기서 3번 Synchronized 인 것 같습니다.
Synchronized 뜻을 확인해 보면 동기화라는 뜻이라는 걸 알 수 있습니다.
무슨 말인지 복잡하죠. 해석하면 다음과 같습니다.
호출자(Caller)는 호출 대상자(Callee)의 결과에 관심이 있습니다.
호출 대상 메소드의 결과에 따라 다음 Action을 수행해야 하기 때문에 호출 대상자에 결과에 관심을 갖는다.
Async
그럼 Async는 무엇일까요? Async도 Sync와 반대되는 개념으로 다음과 같습니다.
호출자(Caller)는 호출 대상자(Callee)의 결과에 관심을 갖지 않습니다.
호출 대상 메소드의 수행 결과 및 종료를 호출자에게 바로 알려주지 않고 결과가 완료되면 그때 알려줍니다.
그럼 정리를 한번 다시 해보겠습니다.
- 블록킹(Blocking) & 논블록킹(Non-Blocking)
- 블로킹 - 호출 대상자(Callee)가 자신이 할 일을 모두 마칠 때까지 제어권을 계속 가지고 있어 호출자(Caller)에게 제어권을 바로 돌려주지 않는 방식
- 논 블록킹 - 호출 대상자(Callee)가 자신이 할 일을 끝내지 않아도 제어권을 호출자(Caller)에게 반환
- 동기 - 호출자(Caller)는 호출 대상자(Callee)의 결과에 관심이 있습니다.
- 비동기 - 호출자(Caller)는 호출 대상자(Callee)의 결과에 관심을 갖지 않습니다.
여기서 중요한 부분은 기본적으로 4가지 개념을 혼용해서 사용합니다. 대표적인 자료로 IMB에서 2006년에 나온 자료를 바탕으로 개념이 정립됩니다.
위 내용으로 대표적인 JavaScript 언어를 사용해서 예시를 설명드리겠습니다. 기본적 문법을 어느 정도 안다고 판단하고 작성하겠습니다.
Synchronous & Blocking I/O - 동기식과 블로킹
동기식과 블록킹 개념을 정리하면 다음과 같은 상황이 발생합니다.
직원: 반갑습니다. 스타벅스입니다.
손님: 아이스 아메리카노 한 잔주세요.
직원: 네
직원분 커피를 만드는 중...
손님: 아무것도 하지 않고 직원이 커피를 다 만들 때까지 보고만 있습니다. (언제 끝나지?... 시간이 얼마 없는데...)
직원: 라이언 고객님 주문하신 아이스 아메리카노 나왔습니다.
동기식은 손님이 직원 결과의 관심을 갖습니다. 하지만 직원은 자신의 작업을 처리하기 바빠서 손님에게 관심을 가질 수 없습니다.
Synchronous와 Blocking 방식에서는 손님이 Sync, 직원이 Blocking입니다.
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const customerOrdersCoffee = async () => {
await employeeTakesTheOrder();
console.log(
"employeeTakesTheOrder 함수가 끝날때 까지 로그가 실행 안됩니다! ㅠ.ㅠ "
);
};
//호출 대상자
const employeeTakesTheOrder = async () => {
console.log("동기식 블록킹 방식 - 작업 중...");
await delay(1000);
console.log("동기식 블록킹 방식 - 작업 완료!");
};
customerOrdersCoffee(); // 호출자
/**
* @description 실행 결과
* 1. 동기식 블록킹 방식 - 작업 중...
* 2. 동기식 블록킹 방식 - 작업 완료!
* 3. staffOrderBlocking 함수가 끝날때 까지 로그가 실행 안됩니다! ㅠ.ㅠ
*/
Synchronous & Non-blocking I/O - 동기식과 논 블로킹
동기식 그리고 논 블록킹 방식에서 예를 들면 다음과 같습니다.
직원: 반갑습니다. 스타벅스입니다.
손님: 아이스 아메리카노 한 잔주세요.
직원: 네
직원분 커피를 만드는 중...
손님: 혹시 제가 급해서 그러는데... 언제쯤 될까요?
직원: 밀려 있는 주문이 있어서요... 잠시만요...
손님: 확인 되셨을까요?
직원: 아직이요...
손님: 급해서 그러는데 빨리 좀 확인 부탁드립니다.
직원: 바로 드리겠습니다.
손님: 네 부탁드리겠습니다.
직원: 여기 있습니다.
손님: 감사합니다.
동기식은 손님이 직원 결과의 관심을 갖습니다. 하지만 직원은 자신의 다른 작업들을 처리하기 바빠서 손님 요청에 집중할 수 없습니다. 하지만 문의할 때마다 원하는 결과가 아닌 다른 결과를 응답합니다.
Synchronous와 Non-blocking 방식에서는 손님이 Sync, 직원이 Non-blocking입니다.
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const customerOrdersCoffee = async () => {
//최초 호출
employeeTakesTheOrder();
const request = true;
while (request) {
await delay(1000); //1초의 한번씩 물어본다.
const result = getEmployeeTakesTheOrderStatus("완료 하셨나요? ");
if (result) {
break;
}
}
};
//호출 대상자
let task = false;
const employeeTakesTheOrder = () => {
console.log("문의하기");
new Promise((resolve) => {
setTimeout(() => {
task = true;
resolve();
}, 3000);
});
};
//호출 대상자 상태
const getEmployeeTakesTheOrderStatus = (message) => {
console.log("문의: " + message + "대답: ", task ? "네" : "아니요");
return task;
};
customerOrdersCoffee(); //호출자
/**
* @description 실행 결과
* 1. 문의하기
* 2. 문의: 완료 하셨나요? 대답: 아니요
* 3. 문의: 완료 하셨나요? 대답: 아니요
* 4. 문의: 완료 하셨나요? 대답: 네
*/
Asynchronous & Blocking I/O - 비동기식과 블록킹
비동기식 그리고 블록킹 방식에서 예를 들면 다음과 같습니다
직원: 반갑습니다. 스타벅스입니다.
손님: 아이스 아메리카노 한 잔주세요.
직원: 네
직원분 커피를 만드는 중...
손님: 휴대폰 하는 중...
직원: 커피 만드는 중... (다른 요청이 들어와도 작업을 하지 못함...)
손님: 전화 와서 전화를 받는 중...
직원: 커피를 다 만들어서 커피 픽업대에 커피를 내려놓는다. - 라이언 고객님 주문하신 아메리카노 나왔습니다.
손님: 커피가 다 됐다는 걸 알고 찾아간다.
비동기식은 손님이 직원에게 관심을 갖지 않습니다. 그리고 직원도 자신이 만들어야 하는 커피를 열심히 만들고 다 만들면 나중에 손님에게 알려줍니다.
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const customerOrdersCoffee = () => {
employeeTakesTheOrder();
console.log("employeeTakesTheOrder 함수가 끝나던 말던 로그는 실행되요 ^.^ ");
};
//호출 대상자
const employeeTakesTheOrder = async () => {
console.log("비동기식 블록킹 방식 - 작업 중...");
await delay(1000);
console.log("비동기식 블록킹 방식 - 작업 완료!");
};
/**
* @description 실행 결과
* 1. 비동기식 블록킹 방식 - 작업 중...
* 2. employeeTakesTheOrder 함수가 끝나던 말던 로그는 실행되요 ^.^
* 3. 비동기식 블록킹 방식 - 작업 완료!
*/
customerOrdersCoffee(); //호출자
Asynchronous & Non-blocking I/O (AIO) - 비동기식과 논-블록킹
비동기식 그리고 논-블록킹 방식에서 예를 들면 다음과 같습니다
직원: 반갑습니다. 스타벅스입니다.
손님: 아이스 아메리카노 한 잔주세요.
직원: 네
직원분 커피를 만드는 중...
손님: 휴대폰 하는 중...
직원: 커피 만드는 중... (다른 고객님이 주문을 요청함.. 주문받고 다시 커피 만든다.)
손님: 전화 와서 전화를 받는 중...
직원: 커피를 다 만들어서 커피 픽업대에 커피를 내려놓는다. - 라이언 고객님 주문하신 아메리카노 나왔습니다.
손님: 나중에 커피가 다 됐다는 걸 알고 찾아간다.
비동기식은 손님이 직원에게 관심을 갖지 않습니다. 그리고 직원도 자신이 만들어야 하는 커피를 열심히 다가 다른 주문이 들어오면 해당 주문을 처리한 후 다시 커피를 만듭니다. 커피를 다 만들면 나중에 손님에게 알려줍니다.
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const customerOrdersCoffee = async () => {
await employeeTakesTheOrder();
console.log("employeeTakesTheOrder 함수가 끝나던 말던 로그는 실행되요 ^.^ ");
};
//호출 대상자
const employeeTakesTheOrder = async () => {
console.log("비동기식 논-블록킹 - 작업 중...");
setTimeout(() => {
console.log("에효... 이제 작업을 끝났다...");
}, 3000);
};
customerOrdersCoffee(); // 호출자
/**
* @description 실행 결과
* 1. 비동기식 논-블록킹 - 작업 중...
* 2. employeeTakesTheOrder 함수가 끝나던 말던 로그는 실행되요 ^.^
* 3. 에효... 이제 작업을 끝났다...
*/
이번 시간에는 Blocking/Non-blocking & Sync/Async에 대해 알아봤습니다. (정확히... 제가 다시 개념을 정리하는 시간?... 반성합니다.) 시간이 지나서 개념을 까먹을 수 있을 수 있으니 꼭 직접 정리하고 테스트 코드를 만들어서 테스트해보시는 걸 추천드리겠습니다.