[Python] 코루틴 그리고 비동기 프로그래밍
1. 코루틴 Coroutine
파이썬에서 코루틴은 Co(operative) + routine 의 합성어로, 메인 루틴과 서브 루틴이 서로 협력한다는 뜻을 가지고 있다.
즉 시간이 많이 걸리는 메인 루틴은 해당 작업이 완료되게 기다리고, 그 동안 다른 작업은 서브루틴이 처리 가능하다.
이것이 가능한 이유는 코루틴은 실행을 중단했다가 다른 작업을 수행할 수 있고, 다시 중단된 지점부터 작업을 재개할 수 있기 때문이다.
1.1 코루틴과 스레드의 공통점 / 차이점
공통점
- 코루틴과 스레드 둘 다 동시성을 구현하는 방식이다. 동시에 여러 작업을 처리할 수 있도록 한다.
- 작업이 비동기적으로 구현될 수 있음. 특정 작업이 끝나기 기다리지 않고 다음 작업을 수행함.
차이점
- 코루틴은 프로그램에서 직접 제어, 스레드는 OS 레벨에서 스케쥴링을 통해 제어
- 코루틴은 싱글스레드 환경에서 구현됨(한번에 하나의 코루틴), 스레드는 멀티스레드 환경에서 구현됨. 여러 스레드를 동시 실행.
- 코루틴은 I/O Bound 작업에 적합. 스레드는 CPU Bound 작업에 적합.
- 코루틴은 사용자가 명시한 지점에(await, yield)서 전환됨. 스레드는 OS가 자동으로 실행중단, 전환함.
2. 비동기적 프로그래밍
DB와 함께 사용되는(설령 DB와 같이 사용되지 않는다 하더라도) 백엔드 서버는 다음과 같은 특징 때문에 비동기적으로 구현되는것이 필수적이다.
- 다수의 클라이언트로부터 다수의 요청을 처리함.
- 시간이 오래 걸리는 I/O Bound (파일 입출력, 네트워크 요청 등)
예를 들어 동기적 방식으로 DB에서 데이터를 읽어온다고 가정해보자.
1
2
클라이언트 1의 요청 처리 시작 -> 클라이언트 1의 I/O 작업 대기 -> 클라이언트 1 요청 처리 완료
클라이언트 2의 요청 처리 시작 -> 클라이언트 2의 I/O 작업 대기 -> 클라이언트 2 요청 처리 완료
이 과정에서 클라이언트 1의 시간이 오래 걸리는 I/O 작업이 끝나기 전 까지 아무 행동도 할 수 없다. 이 과정을 비동기 방식을 통해 구현하면 다음과 같아진다.
1
2
3
4
5
1. 클라이언트 1이 서버에 요청을 보냄.
2. 서버는 클라이언트 1의 요청을 처리하다가 I/O 작업(예: 데이터베이스 조회) 대기 상태로 전환됨.
3. I/O 작업이 진행되는 동안 서버는 CPU를 블로킹하지 않고 다른 작업을 처리할 수 있음.
4. 그 사이에 클라이언트 2의 요청이 서버로 도착하면, 서버는 즉시 클라이언트 2의 요청을 처리 시작.
5. 클라이언트 1의 I/O 작업이 완료되면, 서버는 그 결과를 받아 후속 작업을 처리함.
클라이언트 1의 I/O 작업 대기 중 클라이언트 2의 요청을 받을 수 있다.
아래는 I/O Bound를 모방한 파이썬 코드이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import asyncio
async def handle_client1():
print("클라이언트 1 요청 처리 중")
await asyncio.sleep(3) # I/O 작업을 모방하는 비동기 대기
print("클라이언트 1의 I/O 작업 완료")
async def handle_client2():
print("클라이언트 2 요청 처리 중")
await asyncio.sleep(1) # I/O 작업을 모방하는 비동기 대기
print("클라이언트 2의 I/O 작업 완료")
async def main():
await asyncio.gather(handle_client1(), handle_client2()) # 동시 요청을 가정
# 메인 비동기 실행
asyncio.run(main())
동시에 실행했을 때, 시간이 오래 걸리는 클라이언트 1의 I/O 작업을 대기시키고 클라이언트 2의 요청부터 처리했다.
3. 파이썬에서의 코루틴, 비동기 프로그래밍.
파이썬에서는 다음을 통해 코루틴, 비동기 프로그래밍을 구현한다.
async
파이썬에서 비동기 함수는
async def
로 정의되며, 이것으로 정의된 함수는 코루틴 객체를 반환한다.await
await
키워드는 다른 비동기 함수나 작업이 완료될 때까지 기다리는 역할을 한다.await
키워드로 각각의 코루틴이 완료될때까지 기다리는것이 아닌, 동시에 처리할 수 있도록 한다.
사용 예시는 위의 코드를 참고하면 된다.