코루틴이란?
- 실행의 지연과 재개를 허용 함으로써, non-preemptive(비 선점적) 멀티태스킹을 위한 subroutine을 일반화
- 서브루틴: 함수의 동작이 끝나면 자신을 호출하였던 메인루틴으로 돌아오는 루틴. 하나의 진입점과 하나의 탈출점
- 코루틴: 루틴을 진행하는 중간 멈춰서 특정 위치로 돌아갔다가 다시 원래 위치로 돌아올 수 있는 루틴. (메인 루틴에 종속적이지 않음) 다양한 진입점과 다양한 탈출점
- 비 선점형 멀티태스킹: 병렬이 아닌 병행성(Concurrency). -> 동시에 실행되는 것처럼 보임. CPU 사용권을 뺏을 수 없음. -> 작업 교환 때 비용이 적음.
- 싱글 스레드에서 동작하는 비동기 동시성(Concurrency) 작업
- 멀티스레드를 대체하기 위한 것이 아닌, 스레드에서 대기시간을 최소화하는 것
- CPU와 리소스 낭비 방지 가능
generator 기반 코루틴
- yield 을 통해서 메인 루틴과 서브 루틴간에 값을 이동하며 실행의 지연 및 재개.
generator 기반 코루틴 예시
# 출처: https://velog.io/@soojung61/Python-Coroutine
def coroutine():
total = 0
while True:
num = yield total # total 발생 및 num에 값 저장
total += num
cr = coroutine()
print(next(cr)) # 처음 yield 까지 실행
print(cr.send(3)) # num에 3 저장 후 다음 yield 까지 실행
print(cr.send(2)) # num에 2 저장 후 다음 yield 까지 실행
""" 실행결과
0
3
5
"""
native 코루틴
이것 저것 찾아보다보니.. 내용이 너무 많아서 간단한 기본 예제만 블로깅...!
여태껏 native 코루틴을 잘 모르고 파이썬을 사용 했었다니...
- asyncio 사용하여 구성된 코루틴
- 파이썬에서 제공하는 동시성 프로그래밍
- async/await 구문 사용
- async def 로 코루틴 함수 생성
- await로 awaitable 객체(코루틴 객체, 퓨처 객체, 태스트 객체) 실행 (native 코루틴 안에서만 사용 가능)
- asyncio.run(), asyncio.sleep(), asyncio.wait(), asyncio.gather(), asyncio.Queue() 등 사용
asyn/await 작동 원리
import time
import asyncio
async def say_after(delay, what):
await asyncio.sleep(delay)
print(what)
def sync_say_after(delay, what):
time.sleep(delay)
print(what)
async def main():
s = time.time()
sync_say_after(3, "A")
sync_say_after(2, "B")
print(time.time() - s)
s = time.time()
await say_after(3, "A")
await say_after(2, "B")
print(time.time() - s)
s = time.time()
await asyncio.gather(
say_after(3, "A"),
say_after(2, "B")
)
print(time.time() - s)
asyncio.run(main())
위 코드의 실행 결과는 아래와 같다.
A
B
5.009169340133667
A
B
5.0119383335113525
B
A
3.009953260421753
await을 통해 awaitable 객체가 완료될 때 까지 대기된다. 따라서 동기적으로 진행되는 첫 예시와, 비동기지만 await으로 대기하는 두번째 예시는 비슷한 시간(3초 + 2초)이 소모된다.
세번째 예시의 경우 두 코루틴 함수 동시 실행 후, 늦게 종료되는 say_after(3, "A") 코드를 기다린다. 따라서 3초 정도의 시간만 소모한다. asyncio.gather
task1 = asyncio.create_task(
say_after(3, "A")
)
task2 = asyncio.create_task(
say_after(2, "B")
)
await task1
await task2
이는 task로도 작성이 가능하다. 중요한 점은 await은 코드를 실행하는 것이 아닌, task의 완료를 대기하는 역할을 할 뿐이다. 위 예제에서 task는 생성되자 마자 바로 실행된다. asyncio.create_task
비동기 프로그래밍이 아직 익숙하지 않지만, 현재 하고 있는 프로젝트에 당장 적용시켜보며 native 코루틴과 친해져야겠다.
다음 포스팅은 아마 적용한 코루틴 예시이거나 OS 기초 내용일 것 같다.
Refence
- docs.python.org/ko/3/library/asyncio-task.html
- velog.io/@soojung61/Python-Coroutine
'Computer Science' 카테고리의 다른 글
Process vs Thread 정리 (0) | 2021.03.31 |
---|