비동기(Asynchronous)란?

이해를 돕기 위해 약간은 비현실적인 예를 들어보겠습니다. 여러분들은 10대의 세탁기를 돌리고, 10대의 커피포트에 물을 끓여야 합니다. 이 일을 하기 위한 방법은 아래와 같이 두 가지 방법이 있습니다.

첫번째 방법

  • 1번 세탁기를 돌린다.
  • 1번 세탁기가 완료될 때까지 기다린다.
  • 1번 세탁기에서 빨래를 수거한다.
  • 2번 세탁기를 돌린다.
  • 2번 세탁기가 완료될 때까지 기다린다.
  • 2번 세탁기에서 빨래를 수거한다.
  • ... (위의 과정 반복)
  • 10번 세탁기를 돌린다.
  • 10번 세탁기가 완료될 때까지 기다린다.
  • 10번 세탁기에서 빨래를 수거한다.
  • 1번 커피포트에 물을 끓인다.
  • 1번 커피포트의 물이 끓기를 기다린다.
  • 1번 커피포트의 물로 녹차를 탄다.
  • 2번 커피포트의 물을 끓인다.
  • 2번 커피포트의 물이 끓기를 기다린다.
  • 2번 커피포트의 물로 녹차를 탄다.
  • ... (위의 과정 반복)
  • 10번 커피포트의 물을 끓인다.
  • 10번 커피포트의 물이 끓기를 기다린다.
  • 10번 커피포트의 물로 녹차를 탄다.

두번째 방법

  • 1번 세탁기를 돌린다.
  • 1번 세탁기가 완료되길 기다리지 않고 2번 세탁기를 돌린다.
  • ... (위의 과정 반복)
  • 9번 세탁기가 완료되길 기다리지 않고 10번 세탁기를 돌린다.
  • 10번 세탁기가 완료되길 기다리지 않고 1번 커피포트에 물을 끓인다.
  • 1번 커피포트의 물이 끓길 기다리지 않고 2번 커피포트에 물을 끓인다.
  • ... (위의 과정 반복)
  • 10번 커피포트에 물을 끓인다.
  • 각 작업이 완료되는 순서대로 처리한다. (빨래 수거/녹차 타기)

여러분들은 위의 두 가지 방법 중 어떤 방법을 택하시겠습니까? 당연히 두 번째 방법을 택하실 겁니다.

두 번째 방법으로 일을 처리하는 것을 비동기적 처리라 하고, 첫 번째 방법으로 일을 처리하는 것을 동기적 처리라고 합니다. 비동기적 처리 방식과 동기적 처리 방식의 가장 큰 차이는 기다리는 시간의 존재 여부입니다. 비동기 처리 방식에서는 기다리는 시간을 매우 싫어합니다. 아무것도 하지 않고 기다리는 시간이 발생하면 바로 다른 작업을 시작합니다. (물론 더 할 작업이 없다면 기다리는 시간이 발생할 수밖에 없습니다.) 반면, 동기적 처리 방식에서는 하나의 작업을 시작하면 그 작업이 끝날 때까지 다른 작업을 시작하지 않습니다. 따라서 기다리는 시간이 길면 길수록 엄청난 시간을 낭비하게 됩니다.

프로그래밍에서도 이 개념이 똑같이 적용됩니다. 응답을 기다려야 하는 일이 발생했을 때 기다리지 않고 바로 다른 작업을 하는 것이 비동기적 프로그래밍, 응답이 올 때까지 기다린 이후에 다른 작업을 하는 것을 동기적 프로그래밍이라고 합니다.

멀티쓰레드 vs 비동기

이는 멀티쓰레드 와 비슷하다고 생각하실 수도 있지만, 쓰레드 비동기 처리에는 큰 차이가 있습니다. 위의 예시를 조금 바꿔보겠습니다. 이번에는 10명의 사람에게 각각 한대의 세탁기를 맡게 해서 일을 시킬 수도 있습니다. (커피포트는 생략하겠습니다.) 하지만, 이들은 동시에 일을 할 수는 없습니다. (파이썬의 GIL 때문입니다. 궁금하시면 여기를 확인해주세요!) 이들이 일을 하기 위해서는 다음과 같은 과정을 거칩니다.

  • 사람 1은 1번 세탁기를 돌립니다.
  • 사람 1은 사람 2에게 일을 하라고 전화를 합니다. 사람 1은 하던 일을 멈춥니다.
  • 사람 2는 사람 1의 전화를 받고, 2번 세탁기를 돌립니다.
  • 사람 2는 사람 3에게 일을 하라고 전화를 합니다. 사람 2는 하던 일을 멈춥니다.
  • ...
  • 사람 10은 사람 9의 전화를 받고 10번 세탁기를 돌립니다.
  • 사람 10은 사람 1에게 일을 하라고 전화를 합니다. 사람 10은 하던 일을 멈춥니다.
  • 사람 1은 사람 10의 전화를 받고 일을 하러 갑니다. 그런데 1번 세탁기가 완료되지 않았습니다.
  • 사람 1은 사람 2에게 일을 하라고 전화를 합니다. 사람 1은 하던 일을 멈춥니다.
  • 사람 2는 사람 1의 전화를 받고 일을 하러 갑니다. 그런데 2번 세탁기가 완료되지 않았습니다.
  • 사람 2는 사람 2에게 일을 하라고 전화를 합니다. 사람 2는 하던 일을 멈춥니다.
  • ... (위의 과정 반복)
  • 5번 세탁기가 가장 먼저 완료되었습니다. (가정입니다.)
  • 하지만 현재 일의 통제권을 가진 사람은 사람 1입니다.
  • 사람 1은 사람 2에게, 사람 2는 사람 3에게, ... 사람 4는 사람 5에게 전화를 하면서 통제권을 넘깁니다.
  • 사람 5가 빨래를 수거합니다.
  • ... (위의 과정 반복)
  • 모든 빨래가 수거되었습니다.

2번 세탁기를 돌리기 위해 1번 세탁기가 다 되길 기다리지 않아도 돼서 첫 번째 방법보다는 빠르게 일을 끝낼 수 있을 것 같습니다. 하지만, 비동기 방식에 비해서는 여전히 비효율적인 점이 눈에 보입니다

  1. 다른 사람에게 일을 넘기기 위해 걸리는 시간(전화 시간)이 낭비됩니다.
  2. 작업이 끝나지 않았음에도 일을 확인해야 합니다. 즉, 쓸데없는 리소스 낭비가 있습니다.
  3. 작업이 끝나더라도 해당 사람이 통제권을 가질 때까지 처리할 수 없습니다.

따라서, 일을 시키고(요청하고) 기다려야 하는 경우에는 비동기적인 방식이 다른 방식들에 비해 더 나은 효율을 가질 수 있습니다. 프로그래밍에서는 특히 데이터를 요청하고 응답을 기다리는 네트워크 IO에서 큰 성능 향상을 기대할 수 있습니다.

파이썬에서 비동기 프로그래밍 시작하기

2편에서는 실제 파이썬 코드로 비동기를 구현해보겠습니다. 여기를 눌러주세요!

참고사이트

https://mingrammer.com/translation-asynchronous-python/

https://medium.freecodecamp.org/a-guide-to-asynchronous-programming-in-python-with-asyncio-232e2afa44f6

https://hackernoon.com/a-simple-introduction-to-pythons-asyncio-595d9c9ecf8c