오늘의 주문서
#01 · 첫째 밤
위기
문을 열자마자 손님 여덟 팀. 수셰프 혼자 주문을 하나씩 만든다. 국이 끓는 동안 그냥 서서 기다린다.
목표
주문 여덟 개를 1초 안에 내보낸다.
심야 식당 동시식당
개업 첫날 밤 열 시. 문을 열자마자 손님이 몰려듭니다. 당신은 첫 주문의 국을 불에 올리고, 국이 끓을 때까지 냄비 앞에 서 있습니다. 3분 뒤 첫 그릇을 내고, 두 번째 주문을 시작합니다.
윤서연 셰프가 타이머를 보며 말합니다. "국이 끓는 동안 네 손은 뭘 했지? 아무것도 안 했어. 그게 오늘 밤의 문제야."
국이 끓기를 기다리는 일을 파이썬으로 흉내 내 봅시다. time.sleep(0.3)은 "0.3초 동안 아무것도 하지 않고 기다린다"는 뜻입니다. 3분을 0.3초로 줄였다고 생각하세요.
import time
def make_order(n):
time.sleep(0.3) # 국이 끓기를 기다린다
return f"#{n}"
start = time.perf_counter()
done = [make_order(n) for n in range(1, 9)]
print(f"{time.perf_counter() - start:.2f}s", done)
# 2.40s ['#1', '#2', '#3', '#4', '#5', '#6', '#7', '#8']time.perf_counter()는 시간을 잴 때 쓰는 시계입니다. 두 번 읽어 빼면 그 사이에 흐른 초가 나옵니다.실행할 때마다 끝자리가 조금씩 다를 수 있습니다. 시간을 재는 예제의 결과는 언제나 "대략"입니다.
주방의 일은 두 종류로 나뉩니다.
| 기다리는 일 (I/O) | 손이 바쁜 일 (CPU) | |
|---|---|---|
| 주방에서 | 국 끓기, 오븐 굽기, 배달 기다리기 | 반죽 치대기, 채 썰기 |
| 프로그램에서 | 네트워크 응답, 파일 읽기, 데이터베이스 | 계산, 압축, 이미지 처리 |
| 기다리는 동안 | 사람은 놀고 있다 | 사람이 계속 바쁘다 |
동시성이 시간을 크게 줄여 주는 것은 기다리는 일이 많을 때입니다. 국이 끓는 동안 다른 냄비를 올리면 되니까요. 손이 바쁜 일은 사람이 실제로 더 있어야 빨라지는데, 파이썬에는 여기에 숨은 규칙이 하나 있습니다. 그 얘기는 8교시에 합니다.
두 단어는 자주 섞여 쓰이지만 뜻이 다릅니다.
병렬성은 동시성의 한 방법입니다. 기다리는 일만 많은 주방이라면 손이 하나여도 동시성만으로 충분히 빨라집니다. 아래 타임라인에서 "혼자 순서대로"와 "요리사 여럿"을 번갈아 돌려 보세요. 막대 하나가 주문 하나이고, 가로축이 시간입니다.
주방 타임라인
주문
6
요리사
3
요리
기다림
취소
실패
time.sleep을 하고, 각 주문이 실제로 시작하고 끝난 시각을 기록해 그린 것입니다.주문이 모두 같은 길이라면, 걸리는 시간은 "한 요리사가 가장 많이 맡은 주문 수 × 주문 하나의 시간"입니다.
import math
def estimate(orders, cooks, each=0.3):
rounds = math.ceil(orders / cooks) # 한 요리사가 맡는 최대 주문 수
return round(rounds * each, 2)
for cooks in (1, 3, 4, 8, 16):
print(cooks, estimate(8, cooks))
# 1 2.4
# 3 0.9
# 4 0.6
# 8 0.3
# 16 0.3파이썬에는 여러 일을 동시에 하는 도구가 세 가지 있습니다. 오늘은 맛만 보고, 다음 교시부터 하나씩 깊이 들어갑니다.
import threading
threads = [threading.Thread(target=make_order, args=(n,)) for n in range(1, 9)]
for th in threads:
th.start() # 요리사 여덟 명이 동시에 시작
for th in threads:
th.join() # 모두 끝날 때까지 기다린다
# 약 0.30초import asyncio
async def make_order_async(n):
await asyncio.sleep(0.3) # 기다리는 동안 다른 주문을 본다
return f"#{n}"
async def main():
return await asyncio.gather(*(make_order_async(n) for n in range(1, 9)))
print(asyncio.run(main()))
# ['#1', '#2', '#3', '#4', '#5', '#6', '#7', '#8'] (약 0.31초)| 도구 | 주방으로 치면 | 어울리는 일 | 교시 |
|---|---|---|---|
threading | 같은 주방에 요리사를 여럿 | 기다리는 일, 오래된 코드 | 2~7 |
multiprocessing | 냉장고까지 따로 쓰는 분점 주방 | 손이 바쁜 일(CPU) | 8 |
asyncio | 혼자 수십 테이블을 도는 홀 매니저 | 기다리는 일이 아주 많을 때 | 9~11 |
셰프의 한마디
요리사를 늘리면 빨라지는 건 맞아. 대신 주방이 시끄러워지지. 두 사람이 한 칠판에 동시에 숫자를 적으면 어떻게 될까? 그건 3교시에 직접 보게 될 거야.
요리사를 늘리는 데는 비용이 듭니다.
오늘 밤의 목표는 주문 여덟 개를 1초 안에 내보내는 것이었습니다. 주문이 모두 "기다리는 일"이니, 요리사 여덟 명이 하나씩 맡으면 됩니다.
start = time.perf_counter()
threads = [threading.Thread(target=make_order, args=(n,)) for n in range(1, 9)]
for th in threads:
th.start()
for th in threads:
th.join()
print(f"{time.perf_counter() - start:.2f}s")
# 0.30s셰프가 타이머를 멈춥니다. "2.4초에서 0.3초. 첫날치고 나쁘지 않네. 그런데 요리사가 여덟 명이 되면, 누가 몇 개를 만들었는지는 누가 세지?"
영업 일지 — 첫째 밤. 주문 8건이 모두 기다리는 일이라, 요리사 8명이 나눠 맡아 0.3초에 끝냄. 내일은 요리사를 정식으로 뽑는다.
make_order의 time.sleep(0.3)을 숫자를 더하는 반복문(손이 바쁜 일)으로 바꾸고, 스레드로 돌리면 빨라지는지 재 보세요. 결과가 이상하다면 8교시에서 이유를 알게 됩니다.셰프의 레시피 카드
기다리는 일(I/O)은 동시성으로 크게 빨라진다. 손이 바쁜 일(CPU)은 진짜 병렬이 필요하다.
동시성 = 번갈아 진행, 병렬성 = 같은 순간에 실행.
시간 재기: start = time.perf_counter() … time.perf_counter() - start
도구 셋: threading(요리사 여럿), multiprocessing(주방 여럿), asyncio(혼자 도는 홀).
동시성은 공짜가 아니다: 관리 비용과 새로운 버그가 따라온다.
새 교시·새 강의 소식 받기
이 강의에 새 교시가 더해지거나 이어지는 강의가 열리면 이메일로 알려 드려요.
R
ReflexKR
눌러 보며 배우는 파이썬과 Reflex. 모든 예제는 Reflex 0.10.0에서 직접 실행해 확인했습니다.
ReflexKR은 배너 광고 없이 무료로 운영돼요. 도움이 되셨다면 커피 한 잔으로 응원해 주세요.
© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0
질문과 답변
이 교시에서 궁금한 점을 남겨 주세요. 모든 언어 페이지의 질문이 함께 모입니다.