Sync VS Async, Blocking VS Non-Blocking

SungBum Park·2020년 2월 8일
17

1. Sync VS Async

먼저 Synchronous와 Asynchronous의 어원을 보자. Synchronous의 Syntogether이란 뜻이고, chronotime이다. 따라서 Synchronous는 함께 시간을 맞춘다라는 뜻으로 해석된다. 반면에 Asynchronous는 앞에 A라는 접두사가 붙어 부정하는 형태가 되어 시간을 맞추지 않는 것이라 해석할 수 있다.

Sync와 Async를 다루려면 위 어원에서 볼 수 있듯이 함께 하는 대상이 누구인지, 그 대상들의 시간은 어떻게 다루어지는지 두 가지를 살펴봐야한다.

1.1. Synchronous (동기)

동기는 두 가지 이상의 대상(함수, 애플리케이션 등)이 서로 시간을 맞춰 행동하는 것이다. 예를들어 호출한 함수가 호출된 함수의 작업이 끝나서 결과값을 반화하기를 기다리거나, 지속적으로 호출된 함수에게 확인 요청을하는 경우가 있다.

어떤 대상 A와 B가 있을 때 동기적으로 처리하는 방법 두 가지를 살펴보자.

A와 B가 시작 시간 또는 종료 시간이 일치하면 동기이다. 예를 들어

  • A, B 쓰레드가 동시에 작업을 시작하는 경우 (예를 들면 자바에서 CyclicBarrier)
  • 메서드 리턴 시간(A)과 결과를 전달받는 시간(B)이 일치하는 경우

A가 끝나는 시간과 B가 시작하는 시간이 같으면 동기이다. 예를 들어 자바에서 synchronized와 BlockingQueue가 위와 같은 경우이다.

1.2. Asynchronous (비동기)

비동기는 동기와 반대로 대상이 서로 시간을 맞추지 않는 것을 말한다. 예를 들어 호출하는 함수가 호출되는 함수에게 작업을 맡겨놓고 신경을 쓰지 않는 것을 말한다. 비동기에 대한 자세한 예제는 아래에서 다룰 것이다.

2. Blocking VS Non-Blocking

블록킹/논블록킹을 동기/비동기와 같이 생각하는 경우가 많은데, 이는 서로 관점이 다르다. 블록킹/논블록킹은 직접 제어할 수 없는 대상을 처리하는 방법에 따라 나눈다. 직접 제어할 수 없는 대상은 대표적으로 IO, 멀티쓰레드 동기화가 있다.

2.1. Blocking

Blocking은 직접 제어할 수 없는 대상의 작업이 끝날 때까지 제어권을 넘겨주지 않는 것이다. 예를 들어 호출하는 함수가 IO를 요청했을 때 IO처리가 완료될 때까지 아무 일도 하지 못한 채 기다리는 것을 말한다.

2.2. Non-Blocking

Non-Blocking은 Blocking과 반대되는 개념이다. 직접 제어할 수 없는 대상의 작업 처리 여부와 상관이 없다. 예를 들어 호출하는 함수가 IO를 요청한 후 IO처리 완료 여부와 상관없이 바로 자신의 작업을 할 수 있다.

3. Sync/Async, Blocking/Non-Blocking 예제

위에서 살펴봤듯이, 동기/비동기와 블록킹/논블록킹은 전혀 다른 개념으로 서로 독립적으로 바라봐야한다. 특히 코드를 분석할 때 서로 분리해서 분석해야 한다.

ExecutorService es = Executors.newCachedThreadPool();

String res = es.submit(() -> "Hello Async").get();

위 코드에서 동기/비동기, 블록킹/논블록킹을 분석해보자.

  • es.submit(() -> "Hello Async"): 비동기
    • submit() 메서드 리턴 시간과 Callable의 실행 결과를 받는 시간이 일치하지 않는다.
    • 블록킹/논블록킹을 고려할 대상이 아니다.
  • get(): 동기, 블록킹
    • get() 메서드 리턴 시간과 결과를 가져오는 시간이 일치한다. (동기)
    • 다른 쓰레드의 작업이 완료될 때까지 대기한다. (블록킹)

3.1. 애플리케이션 관점에서 Sync/Async

예를들어 물건을 주문하는 기능에서 물건을 주문하는데 0.5초가 소요되고, 주문 완료 후 메일을 발송하는데 2초가 소요된다고 하자. 동기와 비동기는 이를 처리하는 시간이 서로 다르다.

  • Sync 응답 속도 = 2.5초
  • Async 응답 속도(메일 발송을 Async로 처리) = 0.5초 + 알파

3.2. I/O 관점에서 Sync/Async, Blocking/Non-Blocking

3.2.1. Synchronous Blocking I/O

device = IO.open()
# 이 thread는 데이터를 읽을 때까지 아무 일도 할 수 없음
data = device.read()
print(data)
  • Synchronous: read() 메서드(애플리케이션)가 리턴하는 시간과 커널에서 결과를 가져오는 시간이 일치한다.
  • Blocking: 커널의 작업이 완료될 때까지 대기한다.

3.2.2. Synchronous Non-Blocking I/O

device = IO.open()
ready = False
while not ready:
    print("There is no data to read!")

    # 다른 작업을 처리할 수 있음

    # while 문 내부의 다른 작업을 다 처리하면 데이터가 도착했는지 확인한다.
    ready = IO.poll(device, IO.INPUT, 5) 
data = device.read()
print(data)
  • Synchronous: read() 메서드(애플리케이션)가 리턴하는 시간과 커널에서 결과를 가져오는 시간이 일치한다.
  • Non-Blocking: 애플리케이션으로부터 요청을 받은 커널은 작업 완료 여부와 상관없이 바로 반환하여 제어권을 애플리케이션에게 넘겨준다. 커널의 작업이 완료되면 작업 결과를 애플리케이션에게 반환한다.
  • 대표적인 예로는 멀티플랙싱을 수행하는 select(), epoll() 함수가 있다.

3.3.3. Asynchronous Non-Blocking I/O (AIO)

ios = IO.IOService()
device = IO.open(ios)

def inputHandler(data, err):
    "Input data handler"
    if not err:
        print(data)

device.readSome(inputHandler)
# 이 thread는 데이터가 도착했는지 신경쓰지 않고 다른 작업을 처리할 수 있다.
ios.loop()
  • Asynchronous: readSome() 메서드(애플리케이션)가 리턴하는 시간과 커널에서 결과를 가져오는 시간이 일치하지 않는다.
  • Non-Blocking: 애플리케이션으로부터 요청을 받은 커널은 작업 완료 여부와 상관없이 바로 반환하여 제어권을 애플리케이션에게 넘겨준다. 작업이 끝나면 애플리케이션에게 시그널 또는 콜백을 보낸다.
  • 대표적인 예로는 윈도우에서 멀티플랙싱을 수행하는 IOCP가 있다.(epoll()보다 성능이 좋다.)

3.3.4 Asynchronous Blocking

Asynchronous Blocking 조합은 비효율적이라 직접적으로 사용하는 모델은 없다. 하지만 Asynchronous Non-Blocking 모델 중에서 Blocking 으로 동작하는 작업이 있는 경우 의도와 다르게 Asynchronous Blocking으로 동작할 때가 있다고 한다.

대표적인 예로는 Node.js와 MySQL을 함께 사용하는 경우이다. Node.js는 비동기로 작업하려 하지만 MySQL 드라이버가 Blocking 방식으로 동작하므로 어쩔 수 없이 Asynchronous Blocking 방식으로 동작하게 된다.

4. 정리

  • Synchronous VS Asynchronous
    • 두 가지 이상의 대상(메서드, 작업, 처리 등)과 이를 처리하는 시간으로 구분한다.
    • Synchronous: 호출된 함수의 리턴하는 시간과 결과를 반환하는 시간이 일치하는 경우
    • Asynchronous: 호출된 함수의 리턴하는 시간과 결과를 반환하는 시간이 일치하지 않는 경우
  • Blocking VS Non-Blocking
    • 호출되는 대상이 직접 제어할 수 없는 경우 이를 구분할 수 있다.
    • Blocking: 직접 제어할 수 없는 대상의 작업이 끝날 때까지 기다려야 하는 경우
    • Non-Blocking: 직접 제어할 수 없는 대상의 작업이 완료되기 전에 제어권을 넘겨주는 경우

Reference

profile
https://parker1609.github.io/ 블로그 이전

5개의 댓글

comment-user-thumbnail
2021년 2월 4일

안드로이드에서 OkHttp등의 사용하여 Http request를 보내고 response는 callback에서 처리하는 것은.. async이면서 blocking인가요 아니면 non-blocking인가요?

1개의 답글
comment-user-thumbnail
2021년 3월 18일

안녕하세요. 동기 비동기를 설명할 때, 이해가 안 가는 부분이 있어 질문드립니다.
" 예를 들어 호출한 함수가 호출된 함수의 작업이 끝나서 결과 값을 반환하기를 기다리거나, 지속적으로 호출된 함수에게 확인 요청을 하는 경우가 있다. "
이 부분 입니다.

"호출한 함수가 호출된 함수의 작업" 문장이 이해가 안 갑니다. 호출한 함수가 뭐고 호출된 함수가 뭐고 용어가 이해가 안 가는데 설명해주실 수 있으신가요...?
function bar() {
let a = fs.readFileSync('text.txt');
}
여기서 호출한 함수는 bar고 호출된 함수는 fs.readFileSync() 메서드를 의미하는 건가요?

1개의 답글
comment-user-thumbnail
2022년 1월 21일

정말 많은 도움이 되었습니다.
감사합니다.

답글 달기