먼저 Synchronous와 Asynchronous의 어원을 보자. Synchronous의 Syn는 together이란 뜻이고, chrono는 time이다. 따라서 Synchronous는 함께 시간을 맞춘다라는 뜻으로 해석된다. 반면에 Asynchronous는 앞에 A라는 접두사가 붙어 부정하는 형태가 되어 시간을 맞추지 않는 것이라 해석할 수 있다.
Sync와 Async를 다루려면 위 어원에서 볼 수 있듯이 함께 하는 대상이 누구인지, 그 대상들의 시간은 어떻게 다루어지는지 두 가지를 살펴봐야한다.
동기는 두 가지 이상의 대상(함수, 애플리케이션 등)이 서로 시간을 맞춰 행동하는 것이다. 예를들어 호출한 함수가 호출된 함수의 작업이 끝나서 결과값을 반화하기를 기다리거나, 지속적으로 호출된 함수에게 확인 요청을하는 경우가 있다.
어떤 대상 A와 B가 있을 때 동기적으로 처리하는 방법 두 가지를 살펴보자.
A와 B가 시작 시간 또는 종료 시간이 일치하면 동기이다. 예를 들어
A가 끝나는 시간과 B가 시작하는 시간이 같으면 동기이다. 예를 들어 자바에서 synchronized
와 BlockingQueue가 위와 같은 경우이다.
비동기는 동기와 반대로 대상이 서로 시간을 맞추지 않는 것을 말한다. 예를 들어 호출하는 함수가 호출되는 함수에게 작업을 맡겨놓고 신경을 쓰지 않는 것을 말한다. 비동기에 대한 자세한 예제는 아래에서 다룰 것이다.
블록킹/논블록킹을 동기/비동기와 같이 생각하는 경우가 많은데, 이는 서로 관점이 다르다. 블록킹/논블록킹은 직접 제어할 수 없는 대상을 처리하는 방법에 따라 나눈다. 직접 제어할 수 없는 대상은 대표적으로 IO, 멀티쓰레드 동기화가 있다.
Blocking은 직접 제어할 수 없는 대상의 작업이 끝날 때까지 제어권을 넘겨주지 않는 것이다. 예를 들어 호출하는 함수가 IO를 요청했을 때 IO처리가 완료될 때까지 아무 일도 하지 못한 채 기다리는 것을 말한다.
Non-Blocking은 Blocking과 반대되는 개념이다. 직접 제어할 수 없는 대상의 작업 처리 여부와 상관이 없다. 예를 들어 호출하는 함수가 IO를 요청한 후 IO처리 완료 여부와 상관없이 바로 자신의 작업을 할 수 있다.
위에서 살펴봤듯이, 동기/비동기와 블록킹/논블록킹은 전혀 다른 개념으로 서로 독립적으로 바라봐야한다. 특히 코드를 분석할 때 서로 분리해서 분석해야 한다.
ExecutorService es = Executors.newCachedThreadPool();
String res = es.submit(() -> "Hello Async").get();
위 코드에서 동기/비동기, 블록킹/논블록킹을 분석해보자.
es.submit(() -> "Hello Async")
: 비동기submit()
메서드 리턴 시간과 Callable의 실행 결과를 받는 시간이 일치하지 않는다.get()
: 동기, 블록킹get()
메서드 리턴 시간과 결과를 가져오는 시간이 일치한다. (동기)예를들어 물건을 주문하는 기능에서 물건을 주문하는데 0.5초가 소요되고, 주문 완료 후 메일을 발송하는데 2초가 소요된다고 하자. 동기와 비동기는 이를 처리하는 시간이 서로 다르다.
device = IO.open()
# 이 thread는 데이터를 읽을 때까지 아무 일도 할 수 없음
data = device.read()
print(data)
read()
메서드(애플리케이션)가 리턴하는 시간과 커널에서 결과를 가져오는 시간이 일치한다.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)
read()
메서드(애플리케이션)가 리턴하는 시간과 커널에서 결과를 가져오는 시간이 일치한다.select()
, epoll()
함수가 있다.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()
readSome()
메서드(애플리케이션)가 리턴하는 시간과 커널에서 결과를 가져오는 시간이 일치하지 않는다.epoll()
보다 성능이 좋다.)Asynchronous Blocking 조합은 비효율적이라 직접적으로 사용하는 모델은 없다. 하지만 Asynchronous Non-Blocking 모델 중에서 Blocking 으로 동작하는 작업이 있는 경우 의도와 다르게 Asynchronous Blocking으로 동작할 때가 있다고 한다.
대표적인 예로는 Node.js와 MySQL을 함께 사용하는 경우이다. Node.js는 비동기로 작업하려 하지만 MySQL 드라이버가 Blocking 방식으로 동작하므로 어쩔 수 없이 Asynchronous Blocking 방식으로 동작하게 된다.
안녕하세요. 동기 비동기를 설명할 때, 이해가 안 가는 부분이 있어 질문드립니다.
" 예를 들어 호출한 함수가 호출된 함수의 작업이 끝나서 결과 값을 반환하기를 기다리거나, 지속적으로 호출된 함수에게 확인 요청을 하는 경우가 있다. "
이 부분 입니다.
"호출한 함수가 호출된 함수의 작업" 문장이 이해가 안 갑니다. 호출한 함수가 뭐고 호출된 함수가 뭐고 용어가 이해가 안 가는데 설명해주실 수 있으신가요...?
function bar() {
let a = fs.readFileSync('text.txt');
}
여기서 호출한 함수는 bar고 호출된 함수는 fs.readFileSync() 메서드를 의미하는 건가요?
안드로이드에서 OkHttp등의 사용하여 Http request를 보내고 response는 callback에서 처리하는 것은.. async이면서 blocking인가요 아니면 non-blocking인가요?