비동기 프로그래밍은 왜 필요할까 (with Python)

Dayeon Oh·2023년 5월 21일
1
post-thumbnail

Abstract

비동기(asynchronous) 프로그래밍은 프로그래밍의 실행 순서가 작업의 시작과 완료 과정에서 블락킹(blocking)이 발생하지 않는 것을 의미합니다.

일반적으로 프로그램은 위에서 아래로(실행 순서대로) 동작합니다. 이를 동기식(synchronous) 프로그래밍이라고 합니다. 이 방식에서는 하나의 작업이 완료되어야 다음 작업을 시작할 수 있습니다.

반면, 비동기 프로그래밍은 시간이 오래걸리는 작업에서 기다리지 않고 다음 작업을 수행할 수 있습니다. 이는 데이터를 읽고 쓰거나 기타 프로세스로 인해 지연이 발생하는 상황에서 유용합니다.

이 글은 비동기 프로그래밍을 파이썬에서의 적용 예시와 함께 설명합니다.

Introduction

Synchronous vs Asynchronous

동기 프로그래밍

동기 프로그래밍은 모든 연산이 순차적으로 실행됩니다. 한 연산이 완료될 때 까지, 다음 연산이 기다려지는 것이죠. 덕분에 코드 이해가 쉽고, 디버깅 시 이점이 있습니다.

반면, 한 작업이 다른 작업을 막음으로써 병목현상이 발생할 수 있습니다.
예를 들면 이러한 상황이 있습니다. 큰 데이터를 다른 서버로부터 요청하고 기다리는 동안, 프로그램은 아무런 작업도 수행하지 못하고 멈춰 있어야 합니다.

비동기 프로그래밍

비동기 프로그래밍은 이러한 문제점을 해결할 수 있습니다. 각각의 작업들이 독립적으로 동시에 실행될 수 있기 때문입니다. 동시에 실행된다는 점에서, 마치 일렬로 늘어진 자동차 사이에서 다른 차선으로 나와 주행하는 것과 비슷다고 볼 수도 있습니다. I/O 작업, 네트워크 요청, 디스크 엑세스 등 긴 시간을 요구하는 작업을 처리할 때, 특히 유용합니다. 이런 작업들이 완료될 동안에도 프로그램은 다른 작업들을 계속 진행할 수 있습니다.

단점은 무엇이 있을까요?
복잡성이 증가한다는 점이 비동기 프로그래밍의 단점입니다. 비동기 작업의 완료 순서가 보장되지 않기 때문에, 비동기 패턴(async/await)에 대한 이해가 필요합니다.

Examples

async/await in Python

위에서, 동기/비동기의 장단점을 알아보았습니다. 이번에는 동일한 작업에 대해, 각각의 동기/비동기 파이썬 코드로 비교해보겠습니다. 위에서 이해가 되지 않았다면, 코드를 통한 이해가 오히려 쉬울 수 있으므로 직접 실행해보는 것을 추천합니다.

동기 버전

다음 코드는 각 작업이 순차적으로 완료될 때 까지 기다리는 동기 방식입니다.

Quiz
아래 코드의 함수 main()은 몇 초동안 동작할까요?

import time

def job(number):
    print(f"Job {number} started")
    time.sleep(1)  # represents a long-running task.
    print(f"Job {number} completed")

def main():
    for i in range(5):
        job(i)

if __name__ == "__main__":
    start_time = time.time()
    main()
    end_time = time.time()
    print(f"Total time taken: {end_time - start_time} seconds")

Answer
총 5초가 소요됩니다.
한 번 실행될 때, 1초가 걸리는 job()이라는 함수를 순차적으로 5회 실행됩니다.

비동기 버전

다음 코드는 여러 작업을 동시적으로 처리하는 비동기 코드입니다. asyncio는 파이썬에서 비동기 작업을 수행하기 위한 파이썬 표준 라이브러리입니다.

이번에도 퀴즈가 있습니다.

Quiz
아래 코드의 함수 main()은 몇 초동안 동작할까요?

import time
import asyncio

async def job(number):
    print(f"Job {number} started")
    await asyncio.sleep(1)  # represents a long-running task.
    print(f"Job {number} completed")

async def main():
    tasks = [job(i) for i in range(5)]
    await asyncio.gather(*tasks)


if __name__ == "__main__":

    start_time = time.time()
    asyncio.run(main())
    end_time = time.time()
    
    print(f"Total time taken: {end_time - start_time} seconds")

Answer
총 1초가 소요됩니다.
한 번 실행될 때, 1초가 걸리는 함수job()을 5개의 코루틴 오브젝트로 생성하고 동시적으로 실행합니다.

Conclusion

비동기 프로그래밍은 복잡한 시스템에서 블락킹이 큰 문제가 될 수 있는 상황에서 매우 중요한 개념입니다. 다만, 비동기 프로그래밍이 무조건 좋은 것은 아닙니다. 복잡성과 예측 불가능한 완료 순서로, 코드 복잡성이 높아질 수 있습니다.

그러나, 이런 복잡성을 효과적으로 관리하고, 이러한 이점을 최대화하면 비동기 프로그래밍은 빠른 응답성을 제공하는 데 효과적일 수 있습니다. 설계, 시나리오 등의 상황에 따라 적합한 선택을 하는 것이 중요합니다.

이 글에서는 비동기 프로그래밍의 기본적인 이해와, 파이썬을 이용한 실제 적용 방법을 설명하였습니다. 이 글이 비동기 프로그래밍 이해에 도움이 되면 좋겠습니다.

References

profile
Backend Developer

0개의 댓글