개발이나 공부를 하시면서 I/O 블로킹, 비동기(Asynchronous)에 대해서 많이 들어보셨을 겁니다.
특히 자바스크립트 등의 싱글 스레드 언어를 공부하다 보면, "자바스크립트는 싱글 쓰레드 언어이지만, Non-Blocking I/O 비동기 처리가 가능하다"라는 말이 나오는데요.
저 또한 처음 들었을 때 위 짤(...)처럼 전혀 이해하지 못했는데요! 그래서 먼저 I/O 작업이 무엇인지? Blocking I/O와 Non-Blocking이 무엇인지? 동기와 비동기가 무엇인지 톺아보고 가겠습니다.
I/O 작업이란?
I/O는 하드웨어 기기를 사용해 입출력이 이루어지는 행위를 모두 일컫습니다. 파일을 만들거나 읽는 작업, 네트워크를 따라 정보를 주고받는 작업 등이 될 수 있겠죠.
일반적으로 I/O 작업은 CPU 연산에 비해 매우 느립니다. CPU에 비해 H/W 속도가 엄청 느리기도 하고, 특히 네트워크는 거리와 상태, 속도에 따라 많게는 수 분이 소모되기도 하죠.
또, 이런 I/O 작업들의 예시를 보시면 아시겠지만, 모두 "하드웨어"에 접근해야 합니다. 그런데 프로그램은 직접 하드웨어에 접근할 수 없어서, 운영체제에게 대신 의뢰해야 합니다. 가뜩이나 느린데 운영체제에게 제어권을 넘겨 주고, 완료 시 다시 찾아오는 과정에 시간이 더 소모되게 됩니다 (...)
Blocking I/O vs Non-Blocking I/O
Blocking I/O와 Non-Blocking I/O 방식은
I/O 작업을 기다리는지? 여부가 주요 관심사입니다.
Blocking I/O
Blocking I/O는 프로그램 스레드가 I/O 작업이 끝날 때까지 기다리는 상태입니다.
- 프로그램이 I/O 작업이 필요할 때, 운영체제에 대신 "A" 파일을 읽어달라고 요청합니다.
- 운영체제는 HDD에 접근헤 파일을 읽고, 완료 시 사용자 프로그램에 완료 여부를 통지합니다.
- 프로그램은 그제서야 다시 다른 작업을 수행합니다.
즉, 운영체제가 대신 "A" 파일을 읽기 전 까지는, (1) 번처럼이 Thread는 아무것도 하지 않고 그저 기다리는(...) 상태가 됩니다.
Non-Blocking I/O
Non-Blocking I/O 은 프로그램 스레드가 I/O 작업을 기다리지 않고 먼저 다른 작업을 수행하는 방식입니다.
- 프로그램이 I/O 작업이 필요할 때, 운영체제에 대신 "A" 파일을 읽어달라고 요청합니다.
- 운영체제는 I/O 작업을 시작하고, 완료 여부와 관계없이 즉시 Pending을 응답합니다.
- 프로그램은 다른 작업을 수행하다가, I/O 작업이 완료되면 관련된 작업을 처리합니다.
즉, Non-Blocking I/O 방식으로 "I/O 작업이 성공적으로 시작했는지?"만 먼저 응답받고, 완료되기 전까지 다른 작업을 수행할 수 있게 되었습니다.
비동기와 동기 방식에 대해서
동기(Synchronus)와 비동기(Asynchronus) 방식은
I/O 작업 완료 후, 결과를 누가 처리하는지? 가 주요 관심사입니다.
동기적(Synchronus) 방식
동기(Synchronus) 방식은 I/O 작업이 완료되었을 때, 요청한 스레드가 완료 작업을 처리하는 방식입니다.
그림을 보고 "동기 방식이 그냥 Blocking I/O 아니야?"라는 당연한 의문이 드실 수 있습니다. 그런데 사실 I/O 작업을 기다리지 않고, 다른 작업을 처리하는 Non-Blocking I/O 동기 방식이 존재합니다.
그런데 요청을 기다리지 않는다면, 작업이 완료된 것을 프로그램이 어떻게 알 수 있을까요?
- 동기적 Non-Blocking I/O 방식은 "운영체제에게 수시로 작업이 완료되었는지?" 물어봅니다.
- 운영체제에게 물어보는 행동은 곧 "운영체제에게 제어권을 넘겨주는" 행위입니다. 아까 제어권을 주고받는 행동(Context Switching)이 시간과 자원이 소모된다고 했죠?
만약 중간에 작업이 너무 길어진다면 어떻게 될까요? 이 긴 작업이 완료될 때까지 기다려야 합니다.
사실, 그래서 Non-Blocking I/O와 동기적 방식은 잘 안 쓰이는 편 이에요.
비동기적(Asynchronus) 방식
비동기 방식(Asynchronus)은 I/O 작업이 종료되었을 때, 여건에 따라 다른 스레드가 완료 작업(Callback)을 처리해도 됩니다.
- Thread #1이 I/O 요청을 보냈지만, 완료 시점에 다른 작업을 하고 있으므로 Thread #2가 대신 맡아 처리합니다.
- 다른 스레드는 작업에 대해 알지 못하므로, 요청 시 작업 완료 함수(Callback)를 같이 제공해야 합니다.
- 호출하는 쓰레드는 작업 완료 여부를 신경 쓰지 않고, 다음 작업을 할 수 있습니다.
비동기적 방식도 또한 Blocking-I/O 방식과 같이 쓰일 수 있습니다. 하지만 요청한 스레드가 I/O 작업을 기다릴 때 다른 작업을 수행할 수 없으니, 동기적 + Non-Blocking I/O 방식과 함께 잘 쓰이지 않습니다.
오늘은 I/O Blocking에 대해서, 그리고 동기적 방식과 비동기적 방식에 대해 알아보았습니다. 오늘 알아본 내용을 요약해 보겠습니다.
- Blocking I/O 및 Non-Blocking I/O는 I/O 작업을 기다리는지? 에 대한 여부를 나타냅니다.
- 동기적 방식, 비동기적 방식은 요청한 스레드가 I/O 작업 완료를 처리하는지? 에 대한 여부를 나타냅니다.
- 둘은 완전히 다른 개념입니다. 동기적 방식으로 Non-Blocking I/O를 조합할 수 있고, 그 반대도 할 수 있습니다.
(그냥 잘 안 쓰는 것뿐입니다)
다음 포스팅은 싱글 스레드인 자바스크립트가 어떻게 Non-Blocking I/O 비동기적 방식을 구현할 수 있는지? 알아보겠습니다.