[F-Lab 멘토링 학습]

동기, 비동기, 블로킹, 논블로킹

everydeveloper 2023. 10. 2. 23:52

동기 vs 비동기 & 블로킹 vs 논블록킹

동기 vs 비동기 & 블로킹 vs 논블록킹

동기(Synchronous)와 비동기(Asynchronous), 그리고 블로킹(Block)과 논블로킹(Non-Block) 개념은 소프트웨어 엔지니어링, 특히 웹 개발에서 매우 중요한 개념들입니다. 이들은 시스템 디자인과 프로그래밍에서 다양한 작업 흐름과 데이터 처리 방식을 결정하는 데 큰 영향을 미칩니다.

동기(Synchronous) vs 비동기(Asynchronous)

동기(Synchronous)

동기 방식은 하나의 작업이 완료될 때까지 다음 작업이 기다리는 방식입니다. 이로 인해 프로그램의 실행 흐름이 예측 가능하며, 디버깅이 쉽습니다. 그러나 이 방식은 리소스를 비효율적으로 사용할 수 있습니다.

예시:

  • HTTP 요청과 같은 네트워크 작업에서 결과를 기다릴 때
  • 데이터베이스 쿼리 실행

비동기(Asynchronous)

비동기 방식은 하나의 작업이 완료되지 않아도 다음 작업을 실행할 수 있습니다. 이로 인해 시스템 자원을 효율적으로 사용할 수 있습니다.

예시:

  • AJAX 요청
  • Node.js에서의 파일 시스템 작업

블로킹(Block) vs 논블로킹(Non-Block)

블로킹(Block)

블로킹은 특정 작업이 완료될 때까지 프로그램의 실행이 멈추는 것을 의미합니다. 이는 동기 작업에 흔히 발생합니다.

논블로킹(Non-Block)

논블로킹은 특정 작업의 완료와 상관없이 다른 작업을 진행할 수 있는 것을 의미합니다. 비동기 작업은 대체로 논블로킹입니다.

동기 & 블로킹, 비동기 & 논블로킹?

동기 작업은 대개 블로킹이며, 비동기 작업은 대개 논블로킹입니다. 그러나 이 두 개념은 서로 독립적이므로, 동기 논블로킹이나 비동기 블로킹 같은 조합도 가능합니다.

예시

  • 동기 블로킹: 표준 입력에서 사용자 입력을 기다림
  • 동기 논블로킹: 설문조사 앱에서 답변을 기다리되, 다른 UI 작업 가능
  • 비동기 블로킹: 어렵게 찾을 수 있으나, 특별한 케이스에서 볼 수 있음
  • 비동기 논블로킹: 웹 서버에서 여러 클라이언트 요청을 동시에 처리

 

이러한 개념들을 충분히 이해하고 활용한다면, 특히 비동기 및 논블로킹 기술을 적절히 사용하여 훨씬 더 효율적인 시스템을 구축할 수 있을 것입니다.

동기 방식이 비효율적이라 불릴만한 이유

동기 방식이 리소스를 비효율적으로 사용하는 이유는 주로 작업 간 의존성과 실행 흐름의 일렬성 때문입니다. 동기 시스템에서는 한 작업이 완료될 때까지 다음 작업이 대기해야 하므로, 여러 작업을 병렬로 처리할 수 있는 기회를 놓칠 수 있습니다. 이러한 특성은 다음과 같은 상황에서 리소스 비효율성을 초래할 수 있습니다.

CPU 자원의 비효율적 활용

동기 작업을 처리하는 동안 CPU는 다른 작업을 처리할 수 없으므로 대기 시간이 발생할 수 있습니다. 이렇게 되면 CPU가 놀게 되어 리소스가 낭비됩니다.

메모리 자원의 비효율

동기 방식에서는 종종 불필요한 데이터를 버퍼(임시 메모리 공간)에 저장해야 할 필요가 생깁니다. 이는 메모리 사용량을 늘리고, 시스템의 전반적인 효율성을 저하시킵니다.

I/O 작업과 네트워크 대기 시간

입출력(I/O) 작업, 특히 네트워크 요청 같은 경우 응답 시간이 오래 걸릴 수 있습니다. 동기 방식에서는 이러한 작업이 완료될 때까지 다른 작업이 대기해야 하므로, 이 시간 동안 리소스가 낭비될 수 있습니다.

스레드 및 프로세스 관리

동기 작업을 병렬로 처리하려면 별도의 스레드나 프로세스를 생성해야 할 수도 있습니다. 이러한 리소스는 오버헤드를 발생시키고, 많은 수의 스레드나 프로세스를 관리하기 어렵게 만듭니다.

비효율적인 리소스 사용은 특히 대규모 시스템에서나 고성능을 요구하는 애플리케이션에서 문제가 될 수 있습니다. 이러한 이유로, 비동기 프로그래밍 모델은 파일 시스템 액세스, 네트워크 요청, 데이터베이스 쿼리와 같은 블로킹 가능성이 있는 작업에서 자주 사용됩니다.

비동기

비동기(Asynchronous) 프로그래밍은 동시에 여러 작업을 처리할 수 있도록 설계된 프로그래밍 패러다임입니다. 비동기 모델에서는 하나의 작업이 완료되지 않았더라도 다른 작업을 시작할 수 있습니다. 이러한 특성 때문에 시스템 자원을 효율적으로 활용할 수 있습니다. 아래에 비동기 프로그래밍의 주요 특징과 장단점, 그리고 활용 사례를 설명하겠습니다.

비동기의 주요 특징

  1. 병렬 처리: 여러 작업을 동시에 처리할 수 있어 시스템의 처리 능력을 향상시킵니다.
  2. 비블로킹: 하나의 작업이 끝나기를 기다리지 않고, 다른 작업을 진행할 수 있습니다.
  3. 이벤트 루프와 콜백: 대표적인 비동기 프로그래밍 모델에서는 이벤트 루프를 사용하여 콜백 함수를 실행합니다.
  4. 프로미스, async/await: 콜백 지옥을 피하기 위해 프로미스나 async/await 같은 고급 문법을 사용할 수 있습니다.

장점

  1. 자원 효율성: CPU와 메모리 자원을 더 효율적으로 사용할 수 있습니다.
  2. 응답성 향상: 하나의 작업이 지연되더라도 다른 작업에는 영향을 미치지 않으므로 사용자 경험이 향상됩니다.
  3. 성능 최적화: I/O 바운드 작업, 네트워크 작업 등에서 성능을 높일 수 있습니다.

단점

  1. 복잡성: 동기 코드보다 복잡하고 디버깅이 어려울 수 있습니다.
  2. 콜백 지옥: 콜백 함수의 중첩으로 인해 코드의 가독성과 유지보수성이 떨어질 수 있습니다.

활용 사례

  1. 웹 서버: Node.js와 같은 비동기 웹 서버는 높은 트래픽을 효율적으로 처리합니다.
  2. 실시간 애플리케이션: 채팅, 게임, 협업 도구 등에서 빠른 응답 시간을 제공합니다.
  3. 데이터베이스 작업: NoSQL 또는 SQL 데이터베이스에 비동기로 쿼리를 실행할 수 있습니다.
  4. 파일 처리: 비동기로 파일을 읽고 쓰면서 다른 작업을 수행할 수 있습니다.
  5. API 요청: 외부 서비스에 대한 비동기 API 요청을 통해 빠른 응답을 받을 수 있습니다.

비동기 프로그래밍은 AWS 같은 클라우드 환경에서도 매우 유용합니다. Lambda 함수, S3 이벤트, DynamoDB 스트림 등 다양한 서비스에서 비동기 처리를 활용할 수 있기 때문에, 이를 통해 더 높은 확장성과 성능을 달성할 수 있습니다.

동기 방식 vs 비동기 방식 -일반적 경우

동기와 비동기는 각각의 사용 케이스에 따라 적합성이 다르며, 일반적이라는 표현은 상황에 따라 다를 수 있습니다. 그렇지만 전통적으로 동기 방식이 더 직관적이고 쉽게 접근할 수 있기 때문에 초기 개발 단계나 작은 프로젝트에서는 동기 방식이 자주 사용됩니다. 또한 동기 코드는 디버깅이 상대적으로 쉽고, 프로그램의 흐름을 쉽게 이해할 수 있습니다.

동기가 일반적으로 사용되는 경우:

  1. 간단한 CRUD 애플리케이션: 데이터의 생성, 읽기, 업데이트, 삭제가 순차적으로 이루어져야 할 때
  2. 순차적 로직 처리: 알고리즘, 계산 작업 등이 순차적으로 실행되어야 할 때
  3. 트랜잭션: 여러 작업이 원자적(Atomic)으로 수행되어야 할 때

비동기 방식은 복잡한 시스템이나 높은 트래픽, 다양한 I/O 작업이 필요한 상황에서 유용합니다. 비동기 코드는 다소 어려울 수 있지만, 성능과 자원 활용 측면에서 매우 효율적입니다.

비동기가 특이하거나 특별한 경우가 아닌 경우:

  1. 웹 서버: 동시에 여러 요청을 처리해야 하는 경우
  2. 실시간 애플리케이션: 채팅, 온라인 게임 등 실시간 상호작용이 필요한 경우
  3. 데이터 처리 파이프라인: 대량의 데이터를 빠르게 처리해야 하는 경우
  4. 네트워크 통신: API 호출, 웹 스크래핑 등 비동기로 처리하면 대기 시간을 최소화할 수 있는 경우

비동기 프로그래밍은 특별한 상황이나 복잡한 시스템에서만 필요한 것이 아니라, 다양한 어플리케이션과 시스템에서 광범위하게 사용되고 있습니다. 특히 현대의 웹 개발, 클라우드 컴퓨팅, 빅 데이터 처리 등에서는 비동기 방식이 거의 필수적으로 적용되고 있습니다.

결국 동기와 비동기는 각각의 장단점과 적용 케이스가 있으므로, 어떤 방식이 '일반적'이라고 단정지을 수는 없습니다. 상황과 요구 사항에 따라 적절한 방식을 선택하는 것이 중요합니다.

비동기 문제점 or 조심해야 할 점

맞습니다. 비동기 프로그래밍은 작업의 복잡도나 개수가 적을 때에는 매우 효율적이고 직관적일 수 있습니다. 하지만 코드의 복잡성이 증가하면 다음과 같은 문제점이 생길 수 있습니다.

문제점

  1. 코드 가독성: 여러 비동기 작업이 중첩되거나 연결되면 코드의 가독성이 떨어지고, 이해하기 어렵게 됩니다. 이를 "콜백 지옥(Callback Hell)"이라고도 합니다.
  2. 디버깅 어려움: 비동기 코드는 실행 순서가 보장되지 않기 때문에 디버깅이 어렵습니다.
  3. 상태 관리: 여러 비동기 작업이 상호 의존적일 경우, 작업의 상태를 정확히 추적하고 관리하기 어려워집니다.
  4. 에러 처리: 비동기 작업 간에 에러가 발생하면 그 처리가 복잡해질 수 있습니다.

조심해야 할 점

  1. 코드 분리: 가능하면 콜백 함수를 별도의 함수로 분리하여 코드의 가독성을 높입니다.
  2. Promise와 async/await 사용: 이러한 문법을 활용하면 비동기 코드를 동기 코드처럼 직관적으로 작성할 수 있습니다.
  3. 에러 핸들링: try/catch 블록이나 .catch() 메소드를 사용하여 에러를 적절히 처리합니다.
  4. 상태 관리 라이브러리: Redux나 MobX와 같은 상태 관리 라이브러리를 사용하여 복잡한 상태를 관리합니다.

비동기 코드가 복잡해질수록 이러한 문제점과 조심해야 할 사항이 많아지기 때문에, 처음부터 코드의 구조와 흐름을 잘 설계하는 것이 중요합니다. 특히, 대규모 프로젝트나 여러 개발자가 협업하는 환경에서는 이러한 점들을 더욱 신중하게 고려해야 합니다.

콜백 함수

콜백 함수(callback function)는 다른 함수에 인자로 전달되는 함수로, 특정 작업이 완료되면 실행됩니다. 콜백 함수는 주로 비동기 프로그래밍에서 이벤트 루프와 함께 사용되거나, 배열의 고차 함수에서 요소를 처리하는 데 사용됩니다. 다음은 콜백 함수에 대한 주요 개념과 사용 사례, 그리고 주의점을 설명한 내용입니다.

주요 개념

  1. 비동기 콜백: I/O 작업, 네트워크 요청, 타이머 등 비동기 작업이 완료될 때 실행되는 함수입니다.
  2. 고차 함수: 함수를 인자로 받거나 반환하는 함수를 고차 함수라고 합니다. 배열 메서드인 .map(), .filter(), .reduce() 등이 이에 해당합니다.
  3. 이벤트 핸들러: 사용자 입력, DOM 이벤트 등을 처리하기 위해 사용되는 콜백 함수입니다.

사용 사례

  • Node.js에서 파일 읽기
  • const fs = require('fs'); fs.readFile('example.txt', 'utf8', function(err, data) { if (err) throw err; console.log(data); });
  • JavaScript에서 배열 처리
  • const numbers = [1, 2, 3, 4]; const squared = numbers.map(function(x) { return x * x; });

주의점

  1. 콜백 지옥: 여러 개의 콜백 함수가 중첩되어 코드의 가독성과 유지보수성이 떨어지는 문제입니다.
  2. 에러 핸들링: 콜백 함수 내에서 발생하는 에러는 적절히 처리해야 합니다. 미처리된 에러는 프로그램 전체에 영향을 미칠 수 있습니다.
  3. 스코프와 클로저: 콜백 함수가 상위 스코프의 변수를 참조할 때 클로저가 발생합니다. 이로 인해 메모리 사용량이 증가할 수 있으므로 주의가 필요합니다.

콜백 함수는 JavaScript 및 다양한 프로그래밍 언어에서 광범위하게 사용되는 패턴입니다. 그러나 콜백의 중첩이 복잡해질 경우에는 Promise나 async/await 같은 비동기 처리 메커니즘을 사용하여 코드를 더 깔끔하고 관리하기 쉽게 작성할 수 있습니다.

블로킹

블로킹(blocking)이란 특정 작업의 실행이 완료될 때까지 프로그램의 실행이 중지되는 것을 의미합니다. 블로킹 방식은 작업의 순차적 수행을 보장하나, 리소스를 비효율적으로 사용할 수 있습니다. 다음은 블로킹에 대한 몇 가지 주요 사항과 예시, 그리고 그에 따른 장단점입니다.

주요 사항

  1. I/O 블로킹: 파일 입출력, 네트워크 통신 등에서 자주 발생합니다.
  2. CPU 블로킹: 복잡한 계산 작업이 긴 시간 동안 CPU를 점유할 경우 발생할 수 있습니다.
  3. 동기 처리: 블로킹은 주로 동기적으로 작업을 처리할 때 나타납니다.

예시

  • 파일 읽기 (Node.js) 위 코드에서 readFileSync 함수는 파일을 모두 읽을 때까지 다음 라인의 코드(console.log('파일 읽기 완료');)가 실행되지 않습니다.
  • const fs = require('fs'); const data = fs.readFileSync('example.txt', 'utf8'); console.log(data); console.log('파일 읽기 완료');

장단점

장점

  1. 코드 가독성: 블로킹 방식은 직관적이고 순차적인 코드 흐름을 갖습니다.
  2. 디버깅 용이: 오류 발생 시 어디서 문제가 발생했는지 쉽게 파악할 수 있습니다.

단점

  1. 비효율적인 리소스 사용: 블로킹 작업이 진행되는 동안, 프로그램은 다른 작업을 수행할 수 없어 리소스가 낭비될 수 있습니다.
  2. 응답성 저하: 사용자 입장에서는 블로킹 작업이 완료될 때까지 애플리케이션이 '멈춘 것'처럼 느낄 수 있습니다.

블로킹 방식은 작업의 복잡도나 어플리케이션의 요구사항에 따라 적절히 사용되어야 합니다. 특히 병렬 처리가 중요한 서버 환경이나 대규모 시스템에서는 블로킹 방식이 성능 저하를 일으킬 수 있으므로, 논블로킹이나 비동기 방식을 고려해야 할 필요가 있습니다.

블로킹 방식이 가지는 단점들 때문에 많은 현대 어플리케이션들은 논블로킹(non-blocking)이나 비동기(asynchronous) 방식을 선호합니다. 그럼에도 불구하고, 블로킹 방식은 특정 상황에서는 그 자체로 유용하게 사용될 수 있습니다.

특별한 사용 사례

  1. 단일 작업 스크립트: 특정 작업을 한 번만 실행하고 종료하는 스크립트에서는 블로킹이 문제가 되지 않을 수 있습니다.
  2. 초기 설정: 어플리케이션 시작 시 필수적인 리소스를 로드하는 등 초기 설정에는 블로킹 방식이 적절할 수 있습니다.
  3. 리소스 락: 여러 프로세스나 스레드가 동일한 리소스에 접근하는 것을 막기 위해 일부러 블로킹을 사용하기도 합니다.

블로킹 방지 전략

  1. 쓰레딩: 별도의 스레드를 생성하여 블로킹 작업을 수행하면 메인 스레드는 다른 작업을 계속할 수 있습니다.
  2. 비동기 프로그래밍: Node.js, Python의 asyncio 같은 비동기 프로그래밍 라이브러리를 사용하여 블로킹을 회피할 수 있습니다.
  3. 큐 및 버퍼: 작업을 일시적으로 저장하는 큐나 버퍼를 사용하여 블로킹 작업과 비블로킹 작업을 분리할 수 있습니다.

기타 고려사항

  • 에러 핸들링: 블로킹 작업 중에 발생하는 에러는 즉시 처리되어야 합니다. 블로킹 상태에서 에러가 발생하면 그 영향이 크게 확산될 가능성이 높습니다.
  • 시간 제한: 블로킹 작업에 시간 제한을 두어, 일정 시간이 지나도 작업이 완료되지 않으면 자동으로 종료되도록 설정할 수 있습니다.

블로킹은 컴퓨팅의 기본적인 개념 중 하나이며, 어플리케이션의 성능과 사용자 경험에 큰 영향을 미칠 수 있습니다. 따라서 어떤 작업을 블로킹으로 처리할지, 아니면 비동기나 논블로킹 방식으로 처리할지는 신중한 고려와 설계가 필요합니다.

논블로킹

논블록킹(non-blocking)은 특정 작업을 수행하는 동안 프로그램의 실행이 중지되지 않도록 하는 방식입니다. 논블록킹은 I/O, 네트워크 작업, 파일 액세스 등에서 주로 사용되며, 리소스를 더 효율적으로 활용할 수 있습니다. 논블록킹의 핵심 개념, 사용 사례, 그리고 장단점에 대해 다음과 같이 살펴보겠습니다.

핵심 개념

  1. 이벤트 루프: 논블록킹 모델에서는 이벤트 루프를 통해 여러 작업을 동시에 관리합니다. 작업이 완료되면 콜백 함수가 호출됩니다.
  2. 비동기: 논블록킹은 대부분 비동기 방식과 함께 사용됩니다. 하지만 논블록킹과 비동기는 엄밀히 말해 같은 것은 아닙니다.
  3. 폴링과 인터럽트: 작업의 상태를 주기적으로 확인하거나 완료 통지를 받는 방법으로 논블록킹을 구현할 수 있습니다.

사용 사례

  • Node.js에서 파일 읽기이 예시에서 readFile 함수는 논블록킹 방식으로 파일을 읽습니다. 콜백 함수는 파일 읽기가 완료된 후에 실행됩니다.
  • const fs = require('fs'); fs.readFile('example.txt', 'utf8', function(err, data) { if (err) throw err; console.log(data); }); console.log('파일 읽기 시작');
  • Python에서 asyncio 사용이 예시에서 await asyncio.sleep(1)은 1초 동안 멈추지만, 그 동안에도 이벤트 루프는 다른 작업을 수행할 수 있습니다.
  • import asyncio async def main(): print('Hello') await asyncio.sleep(1) print('World') asyncio.run(main())

장단점

장점

  1. 효율적인 리소스 사용: 다수의 작업을 동시에 처리할 수 있어 리소스를 효율적으로 활용합니다.
  2. 높은 처리량: 논블록킹은 고부하 상황에서도 높은 성능을 유지할 수 있습니다.

단점

  1. 복잡성: 논블록킹 로직은 동기 방식보다 코드가 복잡해질 수 있습니다.
  2. 디버깅 어려움: 복수의 작업이 동시에 실행되기 때문에 디버깅이 어려울 수 있습니다.

논블록킹은 특히 높은 동시성이 요구되는 서버 애플리케이션, 네트워크 통신, 데이터베이스 접근 등에서 유용하게 쓰입니다. 하지만 논블록킹 방식은 코드의 복잡성을 증가시킬 수 있으므로, 사용 사례와 요구 사항에 따라 적절한 설계와 구현이 필요합니다.

블로킹 GC, 논블로킹 GC

가비지 컬렉션(Garbage Collection, GC)은 메모리 관리 기술의 하나로, 프로그램에서 더 이상 사용되지 않는 메모리를 자동으로 회수하는 역할을 합니다. GC의 작동 방식은 사용하는 프로그래밍 언어나 실행 환경에 따라 다르며, 블로킹 혹은 논블로킹 방식으로 동작할 수 있습니다.

블로킹 GC

블로킹 GC는 가비지 컬렉션을 수행하는 동안 프로그램의 실행을 일시 중단합니다. 이러한 블로킹 방식은 다음과 같은 특징과 단점을 가집니다.

특징

  1. 간단성: 블로킹 방식은 구현이 비교적 단순합니다.
  2. 안정성: 다른 작업과의 상호 작용 없이 메모리를 안전하게 회수할 수 있습니다.

단점

  1. 응답성 저하: 프로그램이 일시 중단되므로 사용자 경험이나 성능에 영향을 줄 수 있습니다.
  2. 리소스 비효율: CPU나 다른 리소스를 효율적으로 사용하지 못할 가능성이 있습니다.

논블로킹 GC

논블로킹 GC는 가비지 컬렉션 작업을 백그라운드에서 실행하여 프로그램의 중단을 최소화합니다. 이 방식은 다음과 같은 특징과 단점을 가집니다.

특징

  1. 응답성 향상: 프로그램의 실행이 거의 중단되지 않으므로 사용자 경험이나 성능이 개선될 수 있습니다.
  2. 리소스 효율: 다른 작업과 병렬로 수행되므로 CPU나 메모리 등의 리소스를 더 효율적으로 활용할 수 있습니다.

단점

  1. 복잡성: 논블로킹 GC는 블로킹 GC보다 구현이 복잡할 수 있습니다.
  2. 오버헤드: 백그라운드에서 동작하므로 CPU나 메모리 등의 추가 리소스가 필요할 수 있습니다.

대표적인 논블로킹 GC 예로는 Java의 G1(Garbage-First) GC, C#의 .NET GC 등이 있습니다.

결론적으로, GC가 블로킹인지 논블로킹인지는 사용하는 기술과 설정에 따라 다르며, 각 방식은 그에 따른 장단점이 있습니다. 특정 애플리케이션의 요구 사항에 따라 적절한 GC 전략을 선택하는 것이 중요합니다.

그렇다면 블로킹 작업일 때 자바 애플리케이션 전체(프로젝트)가 멈추는 것일까?

블로킹 작동 방식에서 "프로그램이 멈춘다"는 표현은 조금 오해의 소지가 있을 수 있습니다. 블로킹 호출이 일어날 때, 그 호출을 실행한 스레드가 작업이 완료될 때까지 대기 상태에 들어가게 됩니다. 이는 해당 스레드가 더 이상의 작업을 수행하지 않고 대기한다는 의미로, 전체 자바 애플리케이션의 실행이 중단되는 것은 아닙니다.

예를 들어, 멀티스레드 환경에서 하나의 스레드가 블로킹 I/O 작업을 수행한다고 해도, 다른 스레드들은 계속해서 작업을 수행할 수 있습니다. 따라서, "블로킹이면 프로그램이 멈춘다"라는 문장은 "블로킹 연산을 수행하는 스레드가 해당 작업이 끝날 때까지 다른 작업을 수행하지 못한다"라고 이해하는 것이 더 정확합니다.

블로킹의 영향 범위 예시

  1. 단일 스레드 애플리케이션: 단일 스레드에서 블로킹 작업을 수행하면, 그 스레드가 작업을 완료할 때까지 다른 어떠한 작업도 수행하지 못하게 됩니다. 이 경우에는 애플리케이션 전체가 대기 상태에 빠질 수 있습니다.
  2. 멀티스레드 애플리케이션: 하나의 스레드가 블로킹 작업을 수행한다 해도, 다른 스레드들은 이에 영향을 받지 않고 작업을 계속 수행할 수 있습니다. 따라서, 전체 애플리케이션의 작동이 멈추지는 않습니다.
  3. 비동기 또는 논블로킹 애플리케이션: 블로킹 작업 자체가 별도의 스레드나 프로세스, 혹은 이벤트 루프 등을 통해 처리되면, 원래의 작업 흐름은 전혀 멈추지 않고 계속 진행됩니다.

결국 블로킹의 영향은 작업을 수행하는 스레드와 그 스레드가 속한 애플리케이션의 전체 구조에 따라 달라질 수 있습니다. 따라서 블로킹이 전체 애플리케이션에 미치는 영향은 그 적용 범위와 실행 컨텍스트에 따라 다르게 해석되어야 합니다.

단일 스레드 JVM vs 멀티 스레드 JVM

자바에서 스레드 하나가 블로킹 작업을 수행하면 그 스레드는 작업이 완료될 때까지 다른 작업을 처리하지 못합니다. 그러나 이것이 전체 자바 애플리케이션에 어떤 영향을 미치는지는 해당 애플리케이션이 단일 스레드로 동작하는지, 멀티 스레드로 동작하는지에 따라 다릅니다.

단일 스레드 JVM

  • 단일 스레드 애플리케이션에서는 블로킹 작업이 발생하면 그 작업이 완료될 때까지 애플리케이션의 다른 모든 작업이 정지됩니다.
  • 이러한 경우에는 블로킹 작업이 자주 발생한다면 애플리케이션의 성능과 사용자 경험이 크게 저하될 수 있습니다.

멀티 스레드 JVM

  • 멀티 스레드 애플리케이션에서는 하나의 스레드가 블로킹 작업을 수행하더라도, 다른 스레드들은 그에 영향을 받지 않고 계속 작업을 수행할 수 있습니다.
  • 그러나 여러 스레드가 동시에 블로킹 작업을 수행하게 되면, 스레드 풀이 고갈될 위험이 있으며 이 경우 전체 시스템의 성능이 저하될 수 있습니다.

고려사항

  • 멀티 스레드 환경에서도 블로킹 작업이 자주 발생하면 스레드 풀의 크기와 관리, 그리고 스케줄링 전략 등을 잘 고려해야 합니다.
  • 또한, 블로킹 작업과 비블로킹 작업을 적절히 조합하여 애플리케이션의 성능을 최적화할 수도 있습니다.

따라서, 자바 애플리케이션에서 블로킹이 어떤 영향을 미치는지는 애플리케이션의 스레딩 모델과 그에 따른 설계 패턴에 크게 의존합니다. 이러한 측면을 고려하여 애플리케이션을 설계하고 최적화하는 것이 중요합니다.

블로킹 논-블로킹 관련한 제어권

블로킹과 논-블로킹의 가장 큰 차이점 중 하나는 작업을 요청한 후 "제어권"이 어떻게 처리되는지에 있습니다.

블로킹 (Blocking)

  • 제어권 유지: 작업을 요청한 후, 해당 작업이 완료될 때까지 호출한 함수나 스레드는 제어권을 반환받지 못하고 대기 상태에 머무릅니다.
  • 예시: 파일을 읽어오는 작업을 요청한 스레드는 파일이 완전히 읽힐 때까지 다른 작업을 수행할 수 없습니다.
  • 장점과 단점: 코드가 상대적으로 간단하고 이해하기 쉽지만, 리소스를 비효율적으로 사용할 가능성이 높습니다.

논-블로킹 (Non-Blocking)

  • 제어권 반환: 작업을 요청한 후에도 즉시 제어권을 반환받아 다른 작업을 계속할 수 있습니다.
  • 예시: 논-블로킹 소켓을 사용할 경우, 데이터를 전송하라는 명령을 내린 후, 실제로 데이터가 전송되기를 기다리지 않고 다른 작업을 수행할 수 있습니다.
  • 장점과 단점: 리소스를 효율적으로 사용할 수 있지만, 코드가 복잡해질 수 있고 상태 관리가 어려울 수 있습니다.

핵심 포인트

  • 블로킹 모델에서는 작업이 완료될 때까지 제어권을 유지하게 되므로, 해당 작업이 끝날 때까지 다른 작업을 수행할 수 없습니다.
  • 논-블로킹 모델에서는 작업을 시작한 후 즉시 제어권을 반환받으므로, 다른 작업을 병렬로 수행할 수 있습니다.

따라서 제어권이 어떻게 반환되느냐에 따라 프로그램이 블로킹 방식으로 동작할지, 논-블로킹 방식으로 동작할지가 결정됩니다. 이러한 차이는 프로그램의 성능, 응답성, 복잡도 등 여러 측면에 영향을 미치므로 신중한 설계가 필요합니다.

제어권은 해당 코드부분을 실행할 수 있는 권한을 제어권

제어권(Control)이라는 개념은 특정 코드 블록이나 함수, 또는 스레드가 CPU의 실행 시간을 갖고 있는 상태를 의미합니다. 다르게 말하면, 제어권을 가진 코드나 스레드는 CPU가 그 작업을 수행하도록 지시할 수 있습니다.

제어권의 의미

  1. 실행 흐름: 제어권은 프로그램의 실행 흐름을 결정합니다. 어떤 함수나 스레드가 제어권을 가지고 있다면, 해당 함수나 스레드의 코드가 실행됩니다.
  2. 자원 할당: 제어권을 가진 코드나 스레드는 CPU 시간을 할당받아 실제로 작업을 수행합니다.
  3. 상태 변이: 제어권이 있는 동안 해당 코드나 스레드는 변수의 상태를 변경하거나 입출력 작업 등을 수행할 수 있습니다.

블로킹 vs 논-블로킹에서의 제어권

  • 블로킹: 제어권은 특정 작업이 완료될 때까지 현재의 코드 블록 또는 스레드에 머무릅니다. 이 때문에 다른 작업을 수행할 수 없게 됩니다.
  • 논-블로킹: 특정 작업을 시작한 후 제어권은 즉시 반환되며, 이를 통해 다른 작업을 병렬로 수행할 수 있습니다.

제어권은 따라서 현재 실행되고 있는 코드나 스레드가 다음에 어떤 작업을 할 것인지, 또는 어떤 자원을 사용할 것인지를 결정하는 중요한 역할을 합니다. 이러한 제어권의 이동은 프로그램의 성능과 효율성, 그리고 복잡도에 직접적인 영향을 미칩니다.

논블로킹-비동기, 블로킹-동기 비슷하지만 다른 개념

논블로킹-비동기와 블로킹-동기는 서로 다른 두 개의 개념을 각각 나타내지만, 이 둘은 서로 밀접한 관계에 있습니다. 한 관점에서 볼 때, 둘 다 시스템이 어떻게 작업을 스케쥴링하고 실행할 것인지를 결정하는 메커니즘입니다.

논블로킹-비동기 (Non-blocking Asynchronous)

  • 논블로킹: 코드가 실행을 요청하고도 바로 다음 작업으로 넘어갈 수 있습니다. 제어권을 바로 다시 반환합니다.
  • 비동기: 이벤트 루프나 콜백, 프로미스, await/async 등을 통해 나중에 처리될 작업을 등록하고, 현재 작업을 계속합니다.
  • 자바에서의 예: Java의 NIO (Non-blocking I/O) 패키지, CompletableFuture 등이 이에 해당합니다.

블로킹-동기 (Blocking Synchronous)

  • 블로킹: 특정 작업이 끝날 때까지 기다려야 합니다. 그 작업이 끝나야만 제어권이 반환됩니다.
  • 동기: 요청과 응답, 또는 두 작업 이상이 연속적으로 일어나며, 하나가 끝나야 다음 것이 시작됩니다.
  • 자바에서의 예: 기존의 Java I/O 패키지, Thread의 join 메서드, synchronized 블록 등이 이에 해당합니다.

비교

  • 논블로킹-비동기: 높은 확장성, 비교적 복잡한 코드와 디버깅
  • 블로킹-동기: 코드 이해와 디버깅이 쉽지만, 리소스 활용이 비효율적일 수 있음

자바에서는 두 가지 방법 모두 지원하고 있으며, 어떤 방식을 적용할지는 애플리케이션의 요구사항, 특성, 그리고 성능 목표에 따라 결정되어야 합니다. 둘 다 장단점이 있으므로, 적절한 전략과 설계가 필요합니다.

논블로킹인데 비동기가 아닐 때가 있고 블로킹인데 동기가 아닐 떄가 있다는 말

논블로킹과 비동기, 블로킹과 동기는 밀접하게 연관되어 있지만, 항상 같은 것은 아닙니다. 이 개념들은 실행 모델의 다른 측면을 다루기 때문에, 동일한 시스템에서도 다르게 적용될 수 있습니다.

논블로킹인데 비동기가 아닌 경우

논블로킹은 특정 작업을 요청했을 때 즉시 제어권을 반환합니다. 그러나 이것이 반드시 비동기적으로 처리되는 것은 아닙니다. 예를 들어, 논블로킹 I/O에서 데이터를 요청하면 시스템은 즉시 제어권을 반환하지만, 데이터가 준비되면 동기적으로 그 데이터를 사용할 수 있습니다.

블로킹인데 동기가 아닌 경우

블로킹은 작업이 완료될 때까지 제어권을 넘기지 않습니다. 이렇게 해도 해당 작업이 동기적으로 이루어진 것은 아닙니다. 예를 들어, 여러 스레드가 하나의 블로킹 큐에서 작업을 가져간다고 할 때, 작업의 처리 자체는 동시에 이루어질 수 있고 이는 동기적이지 않습니다.

적용 예

자바의 BlockingQueue는 블로킹이긴 하지만, 여러 스레드에서 동시에 데이터를 읽고 쓸 수 있으므로 동기적인 작업만을 수행하는 것은 아닙니다. 반면, 자바의 CompletableFuture는 비동기 작업을 수행하지만, join() 메서드를 통해 동기적으로 결과를 기다릴 수 있습니다.

이처럼 논블로킹과 비동기, 블로킹과 동기는 서로 다른 작업 실행의 측면을 나타내므로, 각각 독립적으로 적용될 수 있습니다. 개발자는 문제의 상황과 요구에 따라 적절한 방법을 선택해야 합니다.

그러면 어떤 함수에서 블로킹 이냐 논 블로킹이냐 각각의 경우에 반환값이 필요한 여부에 따라 동기 비동기 여부가 다시 갈림

함수나 메서드가 블로킹이든 논블로킹이든, 그 반환값의 필요성에 따라 동기와 비동기의 적용 여부가 달라질 수 있습니다.

반환값이 필요한 경우

  • 동기-블로킹: 함수는 결과값을 반환하기 전까지 호출한 코드가 대기합니다. 호출한 코드는 결과값을 즉시 사용할 수 있습니다.
  • 비동기-논블로킹: 함수는 즉시 제어권을 반환하고, 나중에 결과가 준비되면 콜백 함수나 프로미스, 이벤트 등을 통해 결과를 전달합니다.

반환값이 필요하지 않은 경우

  • 동기-블로킹: 함수는 내부 작업이 끝날 때까지 호출한 코드가 대기합니다. 반환값이 없더라도 호출한 코드는 함수의 작업이 완료될 때까지 기다립니다.
  • 비동기-논블로킹: 함수는 내부 작업의 완료와 관계없이 즉시 제어권을 반환합니다. 작업 완료 알림이 필요하다면 콜백 함수나 이벤트를 사용할 수 있습니다.

예시

  • Java의 Future 객체: 비동기 작업을 수행하지만, get() 메서드를 통해 동기적으로 결과를 기다릴 수 있습니다. 이는 블로킹 작업입니다.
  • JavaScript의 setTimeout: 논블로킹이며 비동기입니다. 지정된 시간이 지나면 콜백 함수를 호출합니다. 반환값이 필요하지 않은 경우에 사용됩니다.

이러한 선택은 성능, 사용성, 코드의 복잡성 등 여러 요인을 고려하여 결정되어야 합니다. 따라서 개발 상황과 요구사항에 따라 적절한 방법을 선택하는 것이 중요합니다.

동기의 의미 쉽게 이해하기

"Synchronous"라는 단어는 그리스어의 'syn-' (함께)와 'chronos' (시간)에서 유래했습니다.

  • 접두어 'Syn-': 이 접두어는 '함께' 또는 '동시에'라는 의미를 가집니다.
  • 어근 'Chronos': 이 어근은 '시간'을 의미합니다.
  • 접미사 '-ous': 이 접미사는 대개 '…한', '…하는'과 같은 형용사를 만듭니다.

따라서 "Synchronous"를 직역하면 '동시에 일어나는 시간' 또는 '시간이 일치하는' 등의 의미로 해석할 수 있습니다.

이러한 의미는 컴퓨터 공학과 프로그래밍에서 '동기'라는 용어로 널리 사용됩니다. 여기서 '동기'는 두 개 이상의 작업, 이벤트 또는 프로세스가 시간에 따라 동시에 발생하거나 특정 순서에 따라 실행되어야 함을 의미합니다. 즉, 하나의 작업이 끝나면 그 다음 작업이 실행되는 것과 같은 순차적인 실행을 의미하는 경우가 많습니다.

동기 작업은 일반적으로 블로킹 형태로 구현될 수 있으며, 작업이 완료될 때까지 프로그램이나 스레드가 대기 상태에 머무르게 됩니다. 이는 특정 작업의 완료를 기다리면서 리소스를 할당하게 되므로, 때로는 비효율적일 수 있습니다.

  • 즉 기간을 정확히 일치(동기)화 시킨다는 의미(호출과 응답)도 되겠지만 한번 호출후 응답을 받으야 다음을 진행시킨다(동기)라고 받아들이는게 더 이해가 바른 것 같다.

동기화와 콜백함수

동기화 (Synchronization)

동기화는 여러 프로세스나 스레드가 동일한 자원에 접근할 때 일관성을 유지하기 위한 메커니즘이나 패턴을 의미합니다. 동기화는 주로 뮤텍스(Mutex), 세마포어(Semaphore), 모니터(Monitor) 등을 통해 이루어집니다. 동기화의 목적은 자원의 공유와 병렬 처리를 안정적으로 하기 위한 것이며, 데이터의 일관성을 보장합니다.

예를 들어, 자바에서는 synchronized 키워드를 사용하여 메서드나 블록을 동기화할 수 있습니다. 이렇게 동기화된 영역은 한 번에 하나의 스레드만 접근할 수 있게 됩니다.

콜백 함수 (Callback Function)

콜백 함수는 어떤 함수에 인자로 전달되어, 특정 이벤트가 발생했을 때 실행되는 함수입니다. 콜백은 주로 비동기 프로그래밍에서 사용되며, 이벤트 루프와 밀접한 관련이 있습니다.

JavaScript에서는 이러한 콜백 함수가 널리 사용됩니다. 예를 들어, setTimeout 함수에서 두 번째 인자로 콜백 함수를 넣을 수 있고, 지정한 시간이 지난 후에 해당 콜백 함수가 실행됩니다.

setTimeout(function() {
  console.log("2초가 지났습니다.");
}, 2000);

동기화와 콜백 함수의 관계

동기화와 콜백 함수는 서로 다른 목적을 가지고 있지만, 복잡한 시스템에서는 서로 연관성을 가질 수 있습니다. 예를 들어, 콜백 함수 내에서 공유 자원에 접근해야 할 때 동기화 메커니즘을 사용할 수 있습니다. 또한, 비동기 작업을 동기적으로 처리하고 싶을 때 콜백 함수를 사용하여 동기화할 수도 있습니다.

즉, 동기화는 주로 '공유 자원의 안정성'을 위해, 콜백 함수는 주로 '비동기 작업의 편의성'을 위해 사용되며, 이 둘은 상호 보완적인 관계를 가질 수 있습니다.