일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- subgraph
- 강화학습의 수학적 기초와 알고리듬의 이해
- pinecone
- rag
- tool_binding
- Python
- chat_history
- summarize_chat_history
- 추천시스템
- RecSys
- 밑바닥부터시작하는딥러닝 #딥러닝 #머신러닝 #신경망
- human-in-the-loop
- conditional_edge
- update_state
- rl
- 밑바닥부터 시작하는 딥러닝
- REACT
- 강화학습의 수학적 기초와 알고리듬 이해
- langgrpah
- Ai
- tool_calls
- add_subgraph
- langgraph
- toolnode
- humannode
- LangChain
- conditional_edges
- lcel
- 강화학습
- removemessage
- Today
- Total
타임트리
[Python] 동기와 비동기 본문
동기와 비동기
동기(Sync)와 비동기(Async)
코드가 동기적으로 동작한다는 말은, 코드가 반드시 작성된 순서대로 실행되는 것을 말한다. 반대로 코드가 비동기적으로 동작한다는 말은, 코드가 반드시 작성된 순서 그대로 실행되는 것이 아니라는 것을 의미한다.
이해를 위해 짜장면을 배달하는 배달원의 예시를 들어보자. 하나의 주문에서 배달원이 처리해야 하는 일은 아래와 같이 두 가지가 있다.
- 짜장면을 주문자에게 배달
- 주문자가 다 먹은 그릇을 현관 앞에 놔두면 그릇을 수거
만약 주문자가 3명(A, B, C)의 주문이 접수되어 배달부가 A, B, C의 짜장면을 음식점으로부터 받은 상황이라고 하자. 이때, 위 작업을 동기 방식과 비동기 방식의 관점으로 바라보자.
동기 방식의 경우, 순차적으로 작업이 진행되기 때문에 A에게 배달 후(요청), A가 식사 후 그릇을 내놓을 때까지(응답) 기다리고, 응답을 받고 나서야 다음 배달(요청)이 이루어진다. 따라서 C의 경우 앞의 모든 요청과 응답이 이루어질 때까지 기다리게 된다.
반면, 비동기 방식의 경우 배달(요청)이 중요하기 때문에 먼저 배달(요청)을 다 보내놓고 그 다음 응답을 오는 수서대로 나중에 받게 된다.
이를 코드로 작성하며 확인해보자.
- 동기 코드
- A, B, C가 각각 식사 시간이 총 3초, 4초, 5초이라고 하자. 이때 아래 코드를 실해하면 동기 코드이기 때문에 하나씩 순서대로 작동하게 된다.
- A에게 배달 완료 후, A의 그릇을 수거 완료까지 3초를 기다리고, 마찬가지로 B에게 배달 완료 후 B의 그릇 수거 완료까지 4초를 기다리게 된다.
- 총 12초가량 소요된다.
import time
def delivery(name, mealtime):
print(f"{name}에게 배달 완료")
time.sleep(mealtime)
print(f"{name} 식사 완료, {mealtime}초 소요...")
print(f"{name} 그릇 수거 완료")
def main():
delivery("A", 5)
delivery("B", 3)
delivery("C", 4)
if __name__=="__main__":
start = time.time()
main()
end = time.time()
print("총 소요시간: {:.3f}초".format(end-start))
# 결과
A에게 배달 완료
A 식사 완료, 5초 소요...
A 그릇 수거 완료
B에게 배달 완료
B 식사 완료, 3초 소요...
B 그릇 수거 완료
C에게 배달 완료
C 식사 완료, 4초 소요...
C 그릇 수거 완료
총 소요시간: 12.026초
만약 위 코드를 비동기 방식으로 실행하면 어떻게 될까? 아래 코드를 보자.
- 비동기 코드
- 먼저 요청 순서대로 A에게 배달 → B에게 배달 → C에게 배달 완료
- 이후 그릇을 내놓은 순서대로(응답 순서), B 그릇을 수거 → C 그릇 수거 → A 그릇을 수거
- 배달을 먼저 모두 완료한 뒤, 그릇을 수거하러 가는데 A가 아직 식사중(처리)이기 때문에 B에게 가 그릇을 수거하고, 그 다음 C → A 순으로 그릇을 수거한다.
- 동기 코드의 경우 12초 가량 걸렸던 작업이 5초 가량으로 감소한 것을 확인 가능!
import time
import asyncio
async def delivery(name, mealtime):
print(f"{name}에게 배달 완료")
await asyncio.sleep(mealtime)
print(f"{name} 식사 완료, {mealtime}초 소요...")
print(f"{name} 그릇 수거 완료")
async def main():
await asyncio.gather( # 비동기함수 동시 실행
delivery("A", 5),
delivery("B", 3),
delivery("C", 4)
)
if __name__=="__main__":
start = time.time()
asyncio.run(main())
end = time.time()
print("총 소요시간: {:.3f}초".format(end-start))
A에게 배달 완료
B에게 배달 완료
C에게 배달 완료
B 식사 완료, 3초 소요...
B 그릇 수거 완료
C 식사 완료, 4초 소요...
C 그릇 수거 완료
A 식사 완료, 5초 소요...
A 그릇 수거 완료
총 소요시간: 5.008초
또한, delivery()
함수 내에서 순차적으로 진행되는 것이 아니라, delivery("A", 5)
의 await asyncio.sleep(mealtime)
에서 delivery("B", 3)
와 delivery("C", 4)
로 넘어가고 다시 delivery("B", 3)
의 print(f"{name} 식사 완료, {mealtime}초 소요...")
부터 실행되고.. 이런 방식으로 작동되는 것을 확인할 수 있다. 이렇게 순차적으로 진행하지 않고 비동기식으로 작동하는 함수를 비동기적 함수라고 한다.
위 코드에서처럼 async
, await
키워드를 붙여 작성한 함수를 코루틴 함수(coroutine function) 라고 한다. 즉, 코루틴은 실행 중 일시 중단하고 다른 작업으로 전환하고 나중에 재개할 수 있는 특수한 함수다. 파이썬은 코루틴 함수를 사용해 비동기 로직을 비동기 함수로 만든다.
그럼 비동기 함수가 항상 좋은 것일까에 대해 생각해보자. 정답은 아니다라고 할 수 있다. 비동기 함수는 함수 내부에서 순차적으로 진행되는 것이 아니라 다른 곳으로 넘어갈 수 있도록 해준다. 즉, 순차적으로 진행이 되어야 하는 연산 등에서는 굳이 다른 곳으로 넘어갈 필요가 없기 때문에 오히려 비동기로 넘어갔을 때 코드가 복잡해지거나 오히려 수행 속도가 느려지는 경우가 생길 수 있다. 따라서, 서버에 정보를 요청하거나(Request), I/O bound 프로세스와 같이 딜레이가 발생하는 작업을 잘 구분하고 필요한 곳에 적용해야 한다.
추가적으로 함수를 비동기로 작성했는데, 동기 방식으로 작동하고 싶다면 어떻게 할까? 이러한 경우에는 await asyncio.gather()
부분을 지우고, 각 비동기 함수 앞에 await
키워드를 붙여주면 된다.
await 키워드는 비동기 함수를 동기적으로 진행하도록 하기도 하고, 다른 코루틴으로 넘어가게 하는 기점 역할 등 비동기 함수를 처리할 때 사용하는 키워드
- 비동기 함수의 동기적 수행
import time
import asyncio
async def delivery(name, mealtime):
print(f"{name}에게 배달 완료")
await asyncio.sleep(mealtime)
print(f"{name} 식사 완료, {mealtime}초 소요...")
print(f"{name} 그릇 수거 완료")
async def main():
await delivery("A", 5),
await delivery("B", 3),
await delivery("C", 4)
---
참고: 인프런(파이썬 동시성 프로그래밍 : 데이터 수집부터 웹 개발까지 (feat. FastAPI))
'Today I Learned > 동시성 프로그래밍' 카테고리의 다른 글
[Python] Multi-Threading, Multi-Processing, GIL (0) | 2024.05.26 |
---|---|
[Python] 동시성(concurrency)와 병렬성(parallelism) (0) | 2024.05.26 |
[Python] 프로그램, 프로세스와 스레드 (0) | 2024.05.26 |
[Python] 파이썬 코루틴 (0) | 2024.05.26 |
[Python] 바운드와 블로킹 (0) | 2024.05.26 |