✅ Sync / Async는 호출된 함수의 종료 호출을 호출한 함수가 처리하는지, 호출된 함수가 처리하는지의 관점
이렇게만 봐서는, 이해가 안 될 것 같다. 나라도 그럴 것이다.
2. Blocking과 NonBlocking
2-1. Blocking
→ A함수가 B함수를 호출할 때, B함수가 자신의 작업이 종료되기 전까지 A함수에게 제어권을 돌려주지 않는 것
1.
A함수가 B 함수를 호출하면, 제어권을 B에게 넘긴다.
2.
호출된 B함수는 제어권을 갖고 있기에 함수를 실행한다.
a.
이때! A 함수는 제어권이 없기때문에 (B에게 넘겨주었기 때문에) 함수 실행을 멈춘다.
3.
B함수가 완료되면, A함수에게 제어권을 돌려준다.
a.
잠시 중단되었던 A함수는 다시 함수를 이어나간다.
2-2. NonBlocking
→ A함수가 B함수를 호출할 때, B함수가 제어권을 바로 A함수에게 넘겨주면서, A함수가 다른 일을 할 수 있도록 하는 것
1.
A함수가 B함수를 호출하여 실행시킨다.
a.
이때! 제어권은 계속 A가 가지고 있기 때문에, 호출한 이후에도 A는 자신의 코드를 계속 실행시킨다.
3. Sync와 Async
3-1. Sync
•
플로우가 순차적으로 이뤄져야 함.
•
A함수가 B함수를 호출 할 때, B함수의 결과값이 나왔는지 A함수가 계속해서 관여하는 것.
◦
B함수의 종료 여부를 계속해서 신경 쓴다.
✔️ 결과값 & 제어권의 반환이 동시에 이뤄짐
3-2. Async
•
플로우가 순서에 영향을 받지 않음.
•
A함수가 B함수를 호출 할 때, B함수의 결과를 B함수가 처리하는 것. (Callback 이용)
◦
B함수의 종료 여부를 신경 쓰지 않는다.
✔️ 결과값 & 제어권의 반환이 동시에 이뤄지지 않음
4. 크로스오버 조합
4-1. Blocking - Sync
한 줄 요약 : 가장 보편적으로 이해하고 있는 동기 - 기다릴 가치가 있네 !
A 함수가 B 함수를 호출했을 때, A 함수는 작업을 중단한다. (제어권을 넘겨줬기에 !!) - Blocking
호출된 함수B의 리턴 값을 A함수가 필요로 한다. (함수의 종료와 결과 값 반환을 신경 쓴다.) + 제어권의 반환과 결과 값의 반환이 일치한다. - Sync
🤔언제 쓸까?
→ 다른 작업의 결과가 자신의 작업에 영향을 주는. 즉, 순차적인 의존성이 있는 작업을 처리할 때.
4-2. NonBlocking - Sync
한 줄 요약 : 동기이지만, 동시에 실행이 가능하다 !! (feat. 컨텍스트 스위칭)
A 함수가 B 함수를 호출해도, A 함수는 작업을 계속 이어나간다. - NonBlocking
호출된 함수 B의 종료를 A가 관리한다. + 결과값의 반환과 제어권의 반환이 일치한다. - Sync
결과값의 반환과 제어권의 반환이 일치한다.
“응 아직 안 끝남” 역시 결과값 !!! (안끝났다는 결과값)
🤔언제 쓸까?
→ 브라우저에서 프로그램을 다운로드 할 때 !!
1.
브라우저에서 다운로드 버튼을 누르면 백그라운드에서 다운로드 진행
2.
다운로드 중에 브라우저에서 다른 작업 가능 ! (NonBlocking)
3.
다른작업 중에도, 브라우저는 계속해서 다운로드 상태 확인 (로딩%라든지.. 완료 되었는지.. 실패했는지 .. 등등)
4.
다운로드가 완료 되어야만 바로 다음 작업이 수행됨!
(3&4 Sync)
4-3. NonBlocking - Async
한 줄 요약 : 가장 보편적으로 이해하고 있는 비동기 !! - 각개전투 !
A 함수가 B 함수를 호출해도, A 함수는 작업을 계속 이어나간다. - NonBlocking
B함수의 종료를 A함수가 신경 쓰지 않음(A프로세스에 B의 리턴값이 중요하지 않다.) + 결과값의 반환과 제어권의 반환이 일치하지 않는다. - Async
🤔언제 쓸까?
→ 호출하는 함수에 종료 여부 / 결과 값에 연연하지 않는 & 파일/네트워크 I/O 등 동시 작업을 필요로 하는 프로세스에서 !
4-4. Blocking - Async
한 줄 요약 : 비효율의 끝판왕 !! - 전설의 포켓몬
A 함수가 B 함수를 호출했을 때, A 함수는 작업을 중단한다. - Blocking
B함수의 종료를 A함수가 신경 쓰지 않음(A프로세스에 B의 리턴 값이 중요하지 않다.) . + 결과값의 반환과 제어권의 반환이 일치하지 않는다. - Async
✔️B함수가 끝나기를 기껏 기다려 놓고, 결과값 역시 사용하지 않아버리는 ?!!?!
🤔언제쓸까?
→ 왜 쓸까?
사용하는 경우는 대부분 개발자의 실수인 anti-pattern인 경우라고 한다.
node & mysql 간의 통신에서 사용된다고 하니, 관심이 있다면 한번 찾아보는 걸 권한다.
5. NonBlocking Sync vs NonBlocking Async
가만 살펴보니, NonBlocking Sync와 NonBlocking Async는 다른 작업이 진행 중이더라도(함수가 호출 중 이더라도) 중단하지 않고 프로세스를 이어나갈 수 있다.
그렇다면 여기서 한 가지 의문이 든다.
“우리의 목표는 다른 함수를 호출해도 중단 없이 플로우를 이어나가는 것 아닌가? 복잡하게 Async로 코딩할 필요 없는 거 아니야? ”
사실 이번 발표를 준비하면서 끊임없이 들었던 의문이기도 하다.
그렇다면 NonBlocking Sync와 NonBlocking Async의 결정적인 차이.
다시 말해 무엇을 위해서 Sync / Async를 구분지어 사용&개발 할까?
쉬운 이해를 위해 예를 들어 설명해보겠다.
NonBlocking - Sync
1️⃣프로젝트를 맡고있는 1️⃣팀은 팀장이 업무 A, B, C를 사원 a, b, c에게 시키려고 한다.
이때 해당 업무는 업무 특성상 A업무가 완료되어야 그 다음 업무인 B로 순차적으로 이어나갈 수 있는 상황이다.
👨🏻 팀장 : 사원 a님, A업무 처리해주세요.
(👨🏻 팀장님은 다른 업무 중 …)
👦🏻 사원a : 네, 알겠습니다 ! (A 처리 중..)
👨🏻 팀장 : a님 완료했나요?
👦🏻 사원a : 아직 완료 못했습니다 !
👨🏻 팀장 : a님 완료했나요?
👦🏻 사원a : 완료했습니다 !!
👨🏻 팀장 : 감사합니다. 곧 바로 b님, B업무 처리 부탁드릴게요.
(👨🏻 팀장님은 다른 업무 중 …)
👨🏻🦰 사원b : 네, 알겠습니다 !! (B 처리 중..)
👨🏻 팀장 : b님 B업무 완료했나요?
이후 생략...
NonBlocking - Async
2️⃣프로젝트를 맡고 있는 2️⃣팀은 팀장이 업무 A, B, C를 사원 a, b, c에게 시키려고 한다.
이때 해당 업무A, B, C는 업무 간의 영향이 없다.
👨🏻 팀장 : 사원 a님, A업무 처리해주세요.
👨🏻 팀장 : 사원 b님, B업무 처리해주세요.
👨🏻 팀장 : 사원 c님, C업무 처리해주세요.
(👨🏻 팀장님은 다른 업무 중 …)
👩🏻🦰 사원c : 팀장님, C업무 완료했습니다.
👦🏻 사원a : 팀장님, A업무 완료했습니다.
👨🏻🦰 사원b : 팀장님, B업무 완료했습니다.
1번 케이스를 먼저 살펴보자. 팀장님 (제어권을 가진 main 함수)이 사원들에게 업무를 지시하는 상황이다. 팀장님은 업무를 지시하고 다른 업무를 처리하고 있다(NonBlocking). 하지만 업무 특성상 A업무가 완료되어야 B 업무가 수행될 수 있는 순차적인 경향을 보이고 있다(Sync).
2번 케이스 역시 팀장님은 업무를 지시하고 다른 업무를 처리하고 있다(NonBlocking). 업무 특성상 동시에 진행되어도 무방한 상황이다. 그래서인지 업무가 완료되는 시기도 모두 다르다.
정리하자면
1번 케이스에서 팀장님은 업무를 지시함과 동시에 다른 업무를 진행하고 있다. 하지만 전체 프로젝트 플로우에서 A업무가 완료되어야 B업무를 수행할 수 있는 등 프로젝트가 순차적으로 진행되어야 한다.
2번 케이스 역시 팀장님은 업무를 지시함과 동시에 다른 업무를 진행하고 있다. 하지만 프로젝트 내부 업무 간의 영향이 없는 탓에, 모든 사원들이 각자 맡은 업무를 동시에 진행하고 있다.
마무리
사실 Blocking과 NonBlocking. Sync와 Async의 개념은 지독하게 추상적이라고 생각한다.
어느 레벨에서 바라보느냐의 관점 차이도 있겠고, 여러 포스팅을 찾아보면서 느낀 점이지만 전부 예시를 들어서 설명하고 있고 설명하는 방식이 모두 다르다. 특히 Sync와 Async에서. 그래서 더 헷갈렸던 것 같다.
해당 조합들을 대용량 트래픽 환경에서의 File I/O, Network I/O 등 꼭 필요한 케이스에서는 성능과 부하 분산에 효율적이기 때문에, 꼭 필요하다고 생각한다. 하지만 그렇지 상황에 맞지 않은 경우를 사용하였다가 오히려 역효과를 낼 수도 있다고 생각한다.(실제로 FastAPI 공식문서에서도, 잘 모르겠으면 그냥 def를 쓰라고 권하고있다.)