Blocking vs. Non-blocking, Sync vs. Async, Coroutine
카테고리 없음 2023. 3. 31. 10:07이 둘이 무슨 차이인고 찾아보니, 별 시덥잖은 것까지 다 분류를 해놓은 건 아닌가 하는 생각이 든다.
어떤 함수에서는 다른 함수에서 호출을 할 수 있다. 이건 당연하다.
그런데 함수에 따라서 제어권을 반환하거나 하지 않기도 한다.
제어권을 반환하지 않으면 Blocking이고, 제어권을 반환하면 Non-blocking이다.
제어권을 반환하지 않으면 함수 안에서 호출된 함수가 모두 실행된 후에 아래 코드가 실행된다.
하지만 제어권을 반환하면 호출된 함수가 모두 실행되지 않더라도 아래 코드가 실행된다.
뭔 소리여?
여태까진 Async가 콜백을 가진 어떤 Task를 던져놓고 신경을 안 써도 되는 것 쯤으로 이해하고 있었는데,
사실 알고보니 이건 Async 보다는 Non-blocking의 특징이었고, Async는 그것보다 Task의 처리 순서가 보장되지 않는 것으로 이해하는 것이 더 정확하다.
그럼 반대로 Sync는 처리 순서를 보장한다는 것이고, blocking은 제어권이 호출한 함수로 넘어오지 않기 때문에 자연스럽게 Task의 처리 결과를 대기할 수 밖에 없다.
정리하면, Sync, Async는 Task 처리 순서의 보장여부이고, Blocking, Non-blocking은 Task 처리 결과의 대기 여부인 것 쯤이라고 할 수 있다.
Sync: 처리 순서 보장, Async: 처리 순서 보장되지 않음
Blocking: 처리 결과 대기, Non-blocking: 처리 결과 대기하지 않음
그래서 Blocking과 Async의 조합은 교착상태를 유발할 수 있다. 호출함수는 Task의 처리 결과를 대기하지만, Task의 처리 순서가 보장되지 않기 때문에 호출함수는 무한정 대기를 할 수도 있음. 그러니까 앞에서 등록한 Task이지만 랜덤하게 처리되기 때문에 앞의 결과를 매우 늦게 혹은 영영 못 받을 수 있다. 그래서 이 조합은 헬이다. 제어권이 호출함수에게 없으니 이 교착상태를 수동으로 끊어낼 수도 없다.
Non-blocking과 Async는 Task의 처리 결과도 기다리지 않고 Task 처리 순서도 보장하지 않으므로, 아주 그냥 난장판으로 보이고 앞의 예처럼 교착상태가 발생할 수 있겠으나, 제어권이 호출함수에게 있으므로 교착상태로부터 비교적 자유롭다고 할 수 있다.
Blocking과 Sync는 처리 순서도 보장되고 처리 결과도 기다릴테니 코딩 난이도가 가장 낮다. 하지만 Task가 많아지면 어쩔 수 없이 처리 지연이 발생한다.
마지막으로 Non-blocking과 Sync는 처리 결과를 대기하지 않지만 처리 순서는 보장한다. 실제 프로그래밍을 할 때 이런 경우는... 글쎼 아직 못 본듯.
Node.js는 처리 결과를 대기하지 않는다는 점에서는 Non-blocking이 맞다. 그리고 Node.js는 싱글쓰레드이기 때문에 콜백함수에 의한 처리가 필수적이었으니 이로 인해 자연스럽게 Async의 특징이 구현되었다고 보면 된다. I/O 요청이 먼저 끝나는대로 응답을 해줘야 할테니 처리 순서를 보장할 수 없었을테니까. (함수 내부에서 async, await 키워드를 쓰는 것과는 다른 이야기다.)
그리고 Javscript에서 Fetch나 Promise 같은게 쓰기가 어려운 이유가 이것들이 Non-blocking 방식이기 때문. Non-blocking은 처리 결과를 대기하지 않는 특성상 어쩔 수 없이 스코프가 한정(closing) 되어 호출함수 부분과 결과를 공유할 수 없는 경우가 발생하는데, 이런 특징이 경우에 따라 프로그래밍의 난이도를 높이는 요인이 되곤 함. 물론 이런 것들은 어디까지나 I/O가 필요한 부분에서 사용되는 것이고 I/O가 없는 부분에서는 가독성과 흐름을 헤치기 때문에 Fetch나 Promise는 왠만하면 사용을 지양해야 함.
Blocking, Non-blocking, Sync, Async.. 이건 정말로 더럽게 난해한 용어들이다.
하지만 Blocking, Non-blocking는 처리결과 대기여부, Sync, Async는 처리순서 보장여부 정도로 기억해두면 발톱에 떼낀 정도라도 이해가 쉬울 듯 하다.
-------------
코루틴. 이건 또 뭐야?
Process의 한계점을 보완하기 위해 Thread가 있었다면, 이 Thread를 더 잘게 쪼갤 필요가 있어 Coroutine이라는 게 등장했다. 그러니까 Thread의 또 다른 Thread라고 보면 된다. async라는 키워드와 혼용되어 쓰여서 좀 헷갈리는데, 어쨌든 Thread 안에서 상대적으로 작업(혹은 데이터) 단위가 큰 작업(데이터)을 한 번 더 쪼개어 '동시적으로' 처리하기 위해 등장한 것이라고 보면 된다. 쪼개고 쪼개고 쪼개고.. 뭔 이러다 양자 세계까지 들어가겄다.