줄을 서 있는 동안, 당신은 그냥 쉬고 😴, 차례를 기다리며, 그다지 "생산적인" 일을 하지 않습니다. 하지만 점원은 주문만 받지(음식을 준비하진 않기) 때문에 줄이 빠르게 줄어들어 괜찮습니다.
줄을 서 있는 동안, 여러분은 그냥 쉬고 😴, 차례를 기다리며, 그다지 "생산적인" 일을 하지 않습니다. 하지만 점원은 주문만 받지(음식을 준비하진 않기) 때문에 줄이 빠르게 줄어들어 괜찮습니다.
그 다음 당신 차례가 되면, 당신은 실제로 "생산적인" 일을 합니다. 메뉴를 처리하고, 무엇을 먹을지 결정하고, 짝사랑 상대의 선택을 확인하고, 결제하고, 올바른 현금이나 카드를 냈는지 확인하고, 정확히 청구되었는지 확인하고, 주문에 올바른 항목들이 들어갔는지 확인하는 등등을 합니다.
그 다음 여러분 차례가 되면, 여러분은 실제로 "생산적인" 일을 합니다. 메뉴를 처리하고, 무엇을 먹을지 결정하고, 짝사랑 상대의 선택을 확인하고, 결제하고, 올바른 현금이나 카드를 냈는지 확인하고, 정확히 청구되었는지 확인하고, 주문에 올바른 항목들이 들어갔는지 확인하는 등등을 합니다.
하지만 그 다음에는, 아직 버거를 받지 못했더라도, 버거가 준비될 때까지 기다려야 🕙 하므로 점원과의 작업은 "일시정지" ⏸ 상태입니다.
하지만 번호를 들고 카운터에서 벗어나 테이블에 앉으면, 당신은 짝사랑 상대에게 관심을 전환 🔀 하고, 그에 대한 "작업" ⏯ 🤓 을 할 수 있습니다. 그러면 당신은 다시 짝사랑 상대에게 작업을 거는 매우 "생산적인" 일을 하게 됩니다 😍.
하지만 번호를 들고 카운터에서 벗어나 테이블에 앉으면, 여러분은 짝사랑 상대에게 관심을 전환 🔀 하고, 그에 대한 "작업" ⏯ 🤓 을 할 수 있습니다. 그러면 여러분은 다시 짝사랑 상대에게 작업을 거는 매우 "생산적인" 일을 하게 됩니다 😍.
그 다음 점원 💁 이 카운터 화면에 당신 번호를 띄워 "버거를 만들었어요"라고 말하지만, 표시된 번호가 당신 차례로 바뀌었다고 해서 즉시 미친 듯이 뛰어가지는 않습니다. 당신은 당신 번호를 갖고 있고, 다른 사람들은 그들의 번호를 갖고 있으니, 아무도 당신 버거를 훔쳐갈 수 없다는 것을 알기 때문입니다.
그 다음 점원 💁 이 카운터 화면에 여러분 번호를 띄워 "버거를 만들었어요"라고 말하지만, 표시된 번호가 여러분 차례로 바뀌었다고 해서 즉시 미친 듯이 뛰어가지는 않습니다. 여러분은 여러분 번호를 갖고 있고, 다른 사람들은 그들의 번호를 갖고 있으니, 아무도 여러분 버거를 훔쳐갈 수 없다는 것을 알기 때문입니다.
그래서 당신은 짝사랑 상대가 이야기를 끝낼 때까지 기다린 다음(현재 작업 ⏯ / 처리 중인 작업 🤓 을 끝내고), 부드럽게 미소 지으며 버거를 가지러 가겠다고 말합니다 ⏸.
그래서 여러분은 짝사랑 상대가 이야기를 끝낼 때까지 기다린 다음(현재 작업 ⏯ / 처리 중인 작업 🤓 을 끝내고), 부드럽게 미소 지으며 버거를 가지러 가겠다고 말합니다 ⏸.
그 다음 당신은 카운터로 가서 🔀, 이제 끝난 초기 작업 ⏯ 으로 돌아와 버거를 받고, 감사 인사를 하고, 테이블로 가져옵니다. 이로써 카운터와 상호작용하는 그 단계/작업이 끝납니다 ⏹. 그리고 이는 새로운 작업인 "버거 먹기" 🔀 ⏯ 를 만들지만, 이전 작업인 "버거 받기"는 끝났습니다 ⏹.
그 다음 여러분은 카운터로 가서 🔀, 이제 끝난 초기 작업 ⏯ 으로 돌아와 버거를 받고, 감사 인사를 하고, 테이블로 가져옵니다. 이로써 카운터와 상호작용하는 그 단계/작업이 끝납니다 ⏹. 그리고 이는 새로운 작업인 "버거 먹기" 🔀 ⏯ 를 만들지만, 이전 작업인 "버거 받기"는 끝났습니다 ⏹.
### 병렬 버거 { #parallel-burgers }
이제 이것이 "동시 버거"가 아니라 "병렬 버거"라고 상상해봅시다.
당신은 짝사랑 상대와 함께 병렬 패스트푸드를 먹으러 갑니다.
여러분은 짝사랑 상대와 함께 병렬 패스트푸드를 먹으러 갑니다.
당신은 여러 명(예: 8명)의 점원이 동시에 요리사이기도 하여 당신 앞 사람들의 주문을 받는 동안 줄을 서 있습니다.
여러분은 여러 명(예: 8명)의 점원이 동시에 요리사이기도 하여 여러분 앞 사람들의 주문을 받는 동안 줄을 서 있습니다.
당신 앞의 모든 사람들은, 8명의 점원 각각이 다음 주문을 받기 전에 바로 버거를 준비하러 가기 때문에, 카운터를 떠나지 않고 버거가 준비될 때까지 기다립니다.
여러분 앞의 모든 사람들은, 8명의 점원 각각이 다음 주문을 받기 전에 바로 버거를 준비하러 가기 때문에, 카운터를 떠나지 않고 버거가 준비될 때까지 기다립니다.
이 병렬 버거 시나리오에서, 당신은 두 개의 프로세서(당신과 짝사랑 상대)를 가진 컴퓨터/프로그램 🤖 이며, 둘 다 기다리고 🕙 오랫동안 "카운터에서 기다리기" 🕙 에 주의를 ⏯ 기울입니다.
이 병렬 버거 시나리오에서, 여러분은 두 개의 프로세서(여러분과 짝사랑 상대)를 가진 컴퓨터/프로그램 🤖 이며, 둘 다 기다리고 🕙 오랫동안 "카운터에서 기다리기" 🕙 에 주의를 ⏯ 기울입니다.
패스트푸드점에는 8개의 프로세서(점원/요리사)가 있습니다. 동시 버거 가게는 2개(점원 1명, 요리사 1명)만 있었을 것입니다.
@ -229,7 +229,7 @@ def results():
모든 은행원이 한 고객씩 순서대로 모든 일을 처리합니다 👨💼⏯.
그리고 당신은 오랫동안 줄에서 기다려야 🕙 하며, 그렇지 않으면 차례를 잃습니다.
그리고 여러분은 오랫동안 줄에서 기다려야 🕙 하며, 그렇지 않으면 차례를 잃습니다.
아마 은행 🏦 업무를 보러 갈 때 짝사랑 상대 😍 를 데려가고 싶지는 않을 것입니다.
@ -261,7 +261,7 @@ def results():
그래서 균형을 맞추기 위해, 다음의 짧은 이야기를 상상해보세요:
> 당신은 크고 더러운 집을 청소해야 합니다.
> 여러분은 크고 더러운 집을 청소해야 합니다.
*네, 이게 전부입니다*.
@ -273,9 +273,9 @@ def results():
순서가 있든 없든(동시성) 끝내는 데 걸리는 시간은 같고, 같은 양의 일을 하게 됩니다.
하지만 이 경우, 전(前) 점원/요리사이자 현(現) 청소부가 된 8명을 데려올 수 있고, 각자(그리고 당신)가 집의 구역을 하나씩 맡아 청소한다면, 추가 도움과 함께 모든 일을 **병렬**로 수행하여 훨씬 더 빨리 끝낼 수 있습니다.
하지만 이 경우, 전(前) 점원/요리사이자 현(現) 청소부가 된 8명을 데려올 수 있고, 각자(그리고 여러분)가 집의 구역을 하나씩 맡아 청소한다면, 추가 도움과 함께 모든 일을 **병렬**로 수행하여 훨씬 더 빨리 끝낼 수 있습니다.
이 시나리오에서 (당신을 포함한) 각 청소부는 프로세서가 되어, 맡은 일을 수행합니다.
이 시나리오에서 (여러분을 포함한) 각 청소부는 프로세서가 되어, 맡은 일을 수행합니다.
그리고 실행 시간의 대부분이 기다림이 아니라 실제 작업에 쓰이고, 컴퓨터에서 작업은 <abbrtitle="Central Processing Unit">CPU</abbr>가 수행하므로, 이런 문제를 "CPU bound"라고 부릅니다.
@ -302,7 +302,7 @@ CPU bound 작업의 흔한 예시는 복잡한 수학 처리가 필요한 것들
## `async`와 `await` { #async-and-await }
최신 파이썬 버전에는 비동기 코드를 정의하는 매우 직관적인 방법이 있습니다. 이 방법은 이를 평범한 "순차" 코드처럼 보이게 하고, 적절한 순간에 당신을 위해 "기다림"을 수행합니다.
최신 파이썬 버전에는 비동기 코드를 정의하는 매우 직관적인 방법이 있습니다. 이 방법은 이를 평범한 "순차" 코드처럼 보이게 하고, 적절한 순간에 여러분을 위해 "기다림"을 수행합니다.
결과를 주기 전에 기다림이 필요한 작업이 있고, 이러한 새로운 파이썬 기능을 지원한다면, 다음과 같이 작성할 수 있습니다:
@ -357,19 +357,19 @@ async def read_burgers():
그렇다면, 닭이 먼저냐 달걀이 먼저냐처럼, 첫 번째 `async` 함수는 어떻게 호출할 수 있을까요?
**FastAPI**로 작업한다면 걱정할 필요가 없습니다. 그 "첫" 함수는 당신의 *경로 처리 함수*가 될 것이고, FastAPI는 올바르게 처리하는 방법을 알고 있기 때문입니다.
**FastAPI**로 작업한다면 걱정할 필요가 없습니다. 그 "첫" 함수는 여러분의 *경로 처리 함수*가 될 것이고, FastAPI는 올바르게 처리하는 방법을 알고 있기 때문입니다.
하지만 FastAPI 없이 `async` / `await`를 사용하고 싶다면, 그것도 가능합니다.
### 당신만의 async 코드 작성하기 { #write-your-own-async-code }
### 여러분만의 async 코드 작성하기 { #write-your-own-async-code }
Starlette(그리고 **FastAPI**)는 <ahref="https://anyio.readthedocs.io/en/stable/"class="external-link"target="_blank">AnyIO</a>를 기반으로 하고 있으며, 파이썬 표준 라이브러리 <ahref="https://docs.python.org/3/library/asyncio-task.html"class="external-link"target="_blank">asyncio</a>와 <ahref="https://trio.readthedocs.io/en/stable/"class="external-link"target="_blank">Trio</a> 모두와 호환됩니다.
특히, 코드에서 더 고급 패턴이 필요한 고급 동시성 사용 사례에서는 직접 <ahref="https://anyio.readthedocs.io/en/stable/"class="external-link"target="_blank">AnyIO</a>를 사용할 수 있습니다.
그리고 FastAPI를 사용하지 않더라도, 높은 호환성을 확보하고 그 이점(예: *structured concurrency*)을 얻기 위해 <ahref="https://anyio.readthedocs.io/en/stable/"class="external-link"target="_blank">AnyIO</a>로 자신만의 async 애플리케이션을 작성할 수도 있습니다.
그리고 FastAPI를 사용하지 않더라도, 높은 호환성을 확보하고 그 이점(예: *structured concurrency*)을 얻기 위해 <ahref="https://anyio.readthedocs.io/en/stable/"class="external-link"target="_blank">AnyIO</a>로 여러분만의 async 애플리케이션을 작성할 수도 있습니다.
저는 AnyIO 위에 얇은 레이어로 또 다른 라이브러리를 만들었는데, 타입 어노테이션을 조금 개선하고 더 나은 **자동완성**, **인라인 오류** 등을 얻기 위한 것입니다. 또한 **이해**하고 **자신만의 async 코드**를 작성하도록 돕는 친절한 소개와 튜토리얼도 제공합니다: <a href="https://asyncer.tiangolo.com/" class="external-link" target="_blank">Asyncer</a>. 특히 **async 코드와 일반**(blocking/동기) 코드를 **결합**해야 한다면 아주 유용합니다.
저는 AnyIO 위에 얇은 레이어로 또 다른 라이브러리를 만들었는데, 타입 어노테이션을 조금 개선하고 더 나은 **자동완성**, **인라인 오류** 등을 얻기 위한 것입니다. 또한 **이해**하고 **여러분만의 async 코드**를 작성하도록 돕는 친절한 소개와 튜토리얼도 제공합니다: <a href="https://asyncer.tiangolo.com/" class="external-link" target="_blank">Asyncer</a>. 특히 **async 코드와 일반**(blocking/동기) 코드를 **결합**해야 한다면 아주 유용합니다.
### 비동기 코드의 다른 형태 { #other-forms-of-asynchronous-code }