Blocking IO와 Non-Blocking IO , 동기와 비동기
Blocking
현재 스레드(또는 프로세스)가 어떤 작업을 수행할 때, 그 작업이 완료될 때까지 다른 작업을 할 수 없도록 대기하는 상태
제어권이 호출된 함수에게 넘어가서, 함수가 작업을 끝낼때까지 제어권을 유지합니다.
따라서 호출한 함수는 호출된 함수가 끝나기 전까지 다른 작업을 하지 못하고 대기합니다.
- 파일을 읽거나 쓰는 작업에서 파일 읽기/쓰기가 완료될 때까지 스레드가 대기하는 경우
- 네트워크에서 데이터를 받을 때까지 응답을 기다리며 다른 작업을 하지 못하는 경우
문제
입출력 작업이 완료될때 까지 다른 스레드가 대기해야하기 때문에, 멀티 스레드 환경에서 대기 시간이 오래 걸릴 수 있습니다. 많은 클라이언트의 요청을 동시에 처리할 수 없으며 블록 상태가 발생할 수 있습니다.
Non-Blocking
작업이 즉시 처리되거나, 처리할 수 없는 경우에도 스레드가 대기하지 않고 다른 작업을 계속할 수 있는 상태
제어권이 호출된 함수에게 넘어갔다가, 즉시 호출한 함수로 돌아옵니다. 호출된 함수가 완료되지 않았지만, 제어권을 바로 반환하기 때문에 호출한 함수는 다른 작업을 계속할 수 있습니다.
A 함수가 B 함수를 호출했을 때, B 함수는 바로 A 함수에게 제어권을 돌려주고, A 함수는 B 함수가 끝날 때까지 기다리지 않고 다른 작업을 할 수 있게 됩니다.
차- 작업이 완료될 때까지 대기하지 않고, 작업 큐에 넣어 두고 다른 작업을 수행하는 경우
문제
복잡하며, 콜백 지옥과 같은 문제가 발생할 수 있고, 이벤트를 기반으로 동작하기 때문에 실행 흐름을 추적하거나 디버깅이 어렵습니다. 비동기 작업이 많아질수록 메모리 사용량이 증가할 수 있습니다.
주로 다수의 클라이언트 요청을 비동기로 처리할 때 사용되며, 웹 서버에서 여러 요청을 동시에 처리하고 IO 작업이 완료될 때까지 기다리지 않고 다른 요청을 처리하는데 유용하게 사용됩니다. 대규모 데이터베이스나 파일 처리에서 비동기 IO는 시스템의 처리량을 향상 시키는데 기여할 수 있습니다.
구현 방법에는 자바스크립트에서 콜백함수를 사용하거나 promise 를 사용하는 방법이 있습니다. 또한 자바의 NIO 와 같은 라이브러리를 사용하여 블로킹 IO 를 비동기적으로 처리할 수 있습니다.
동기
작업의 완료를 기다리는 방식으로, 순차적으로 실행되며 한 작업이 끝나야 다음 작업이 시작될 수 있습니다.
작업의 순서가 보장되며, 코드의 실행 순서가 예측 가능합니다.
비동기
작업들이 병렬로 실행될 수 있으며, 어떤 작업이 끝날 때까지 기다리지 않고 다른 작업을 수행합니다. 작업 완료는 나중에 별도로 처리되며, 작업이 완료되면 그에 대한 콜백을 받거나, 나중에 결과를 확인합니다.
Blocking 와 Non-Blocking
함수 호출 후 제어권의 반환 시점
동기 와 비동기
작업 완료 시점에 대한 처리 방식
Promise?
비동기 작업의 결과를 나중에 처리할 수 있는 객체로, 작업의 성공(fulfilled) 또는 실패(rejected)를 다룹니다. 비동기 코드를 더 읽기 쉽게 작성하고 에러 처리를 간편하게 할 수 있습니다. 코드가 복잡해질 수 있고 디버깅이 어려워질 수 있습니다. 또한 비동기 작업을 작업을 과ㅏㄴ리하기 위해 메모리를 추가로 소비하기 때문에 많은 Promise 객체가 생성될 경우 메모리 사용량이 증가합니다.
따라서 모든 비동기에 에러 처리를 해야하며, Promise.all()을 사용할 때, 하나의 Promise라도 실패하면 전체가 실패로 간주되므로, 개별적으로 에러 처리를 할 필요가 있습니다. Promise 내부에서 또 다른 Promise를 반환하는 경우, 코드가 복잡해질 수 있기 때문 async/await를 사용하는 것이 더 적합할 수 있습니다.
NIO (New I/O)
자바의 비동기 IO를 지원하는 API로, 네트워크와 파일 작업을 효율적으로 처리할 수 있게 합니다. Selector와 Channel을 사용하여 비동기적으로 여러 IO 작업을 관리할 수 있습니다.
이벤트 통지 방식
Sync Blocking I/O
입력 요청 등 제어권과 처리 결과를 동시에 반환
- 호출
- 블로킹
- 처리
구현이 간단하고 직관적이며, 스레드가 직적 I/O 작업을 기다리기 때문에 비동기 처리가 필요없습니다.
Sync Non-Blocking I/O
자신의 작업을 하면서 결과가 반환됐는지 확인하는 방식
반환됐을 경우 바로 처리합니다.
- 호출: 스레드가 I/O 작업을 요청합니다. 요청이 즉시 완료되지 않더라도 스레드는 다른 작업을 계속 진행합니다.
- 작업 상태 확인: I/O 작업의 상태를 주기적으로 확인하거나, 결과를 체크합니다.
- 처리: 작업이 완료되면 결과를 처리합니다. 이 과정은 동기적으로 이루어집니다.
스레드가 다른 작업을 계속 수행할 수 있으므로 자원 효율성이 좋으며, 높은 I/O 성능을 요구하는 어플리케이션에서 효과적입니다.
Async Blocking I/O
비동기적으로 I/O 요청을 시작하고, I/O 작업의 완료를 기다리는 블로킹 방식
- 비동기 I/O 요청: I/O 작업을 비동기적으로 시작합니다. 요청을 보내고 제어권을 즉시 반환받습니다.
- 블로킹 대기: I/O 작업의 결과를 기다리는 동안, 스레드가 블로킹되며 대기합니다.
- 결과 처리: I/O 작업이 완료되면, 블로킹된 스레드가 결과를 처리합니다. 이 과정에서 비동기적 요소가 작용할 수 있습니다.
비동기 I/O 요청을 통해 자원의 사용 효율성을 높일 수 있으며, 스레드의 효율성을 높일 수 있습니다.
스레드가 블로킹되는 동안 다른 작업을 수행할 수 없기 때문에 자원의 활용도가 떨어질 수 있으며, 비동기 요청과 블로킹 대기 사이의 복잡성을 관리하기 어려울 수 있습니다.
Async Non-Blocking I/O
I/O 작업의 완료를 기다리지 않고 다른 작업을 동시에 처리할 수 있는 접근 방식
작업 요청 이후 자신의 작업은 그대로 진행. 결과가 반환되어도 바로 처리하는게 아니라 자기 작업이 끝난 이후에 콜백을 통해 추가 작업
- I/O 요청: 스레드는 I/O 작업을 요청하고 즉시 제어권을 반환받습니다. 스레드는 I/O 작업의 완료를 기다리지 않습니다.
- 작업 완료 대기: I/O 작업은 비동기적으로 처리되며, 완료 시 콜백 함수나 Future/Promise 객체를 통해 결과를 받습니다.
- 결과 처리: I/O 작업이 완료되면, 콜백 함수나 Future/Promise 객체를 통해 결과를 비동기적으로 처리합니다.
I/O 작업이 완료될 때까지 스레드가 대기하지 않으므로, 자원 활용도가 높고, 많은 I/O 작업을 동시에 처리할 수 있어 성능이 향상됩니다. 응답 시간이 짧고, 높은 처리량을 달성할 수 있습니다.
비동기 코드의 복잡성이 증가할 수 있습니다. 콜백 지옥(callback hell)이나 상태 관리의 복잡함이 발생할 수 있습니다.