Skip to content

C++ : Asynchronous C++20/23 Programming Practice Using Coroutine and Generator - #359

Draft
kimpro82 wants to merge 12 commits into
masterfrom
cppCoroutineGenerator
Draft

kimpro82 wants to merge 12 commits into
masterfrom
cppCoroutineGenerator

Conversation

@kimpro82

@kimpro82 kimpro82 commented Sep 29, 2026 •

Copy link
Copy Markdown
Owner

Output

=== [PROJECT CAFFEINE-FLOW] INITIALIZING PIPELINE ===

=== SYNCHRONOUS BREW ===
[Hardware] Water successfully boiled to 95°C!
[Barista] Pouring Espresso Cup #1
[Success] Consumed Espresso Unit: 1

[Hardware] Water successfully boiled to 95°C!
[Barista] Pouring Espresso Cup #2
[Success] Consumed Espresso Unit: 2

[Hardware] Water successfully boiled to 95°C!
[Barista] Pouring Espresso Cup #3
[Success] Consumed Espresso Unit: 3

[Synchronous] 3 cups completed in 3.00 seconds.

=== ASYNCHRONOUS BREW ===
[Hardware] Water successfully boiled to 95°C!
[Barista] Pouring Espresso Cup #2
[Success] Consumed Espresso Unit: 2

[Hardware] Water successfully boiled to 95°C!
[Barista] Pouring Espresso Cup #3
[Success] Consumed Espresso Unit: 3

[Hardware] Water successfully boiled to 95°C!
[Barista] Pouring Espresso Cup #1
[Success] Consumed Espresso Unit: 1

[Asynchronous] 3 cups completed in 1.00 seconds.

=== [PROJECT CAFFEINE-FLOW] SHUTTING DOWN SAFELY ===

@kimpro82

Copy link
Copy Markdown
Owner Author

오류 수정

                 from /usr/include/c++/14/stop_token:39,
                 from /usr/include/c++/14/thread:42,
                 from CoroutinePractice.cpp:7:
/usr/include/c++/14/bits/atomic_base.h:103:3: note: candidate: ‘constexpr std::memory_order std::operator|(memory_order, __memory_order_modifier)’
  103 |   operator|(memory_order __m, __memory_order_modifier __mod) noexcept
      |   ^~~~~~~~
/usr/include/c++/14/bits/atomic_base.h:103:26: note:   no known conversion for argument 1 from ‘std::generator<std::expected<int, BrewingError> >’ to ‘std::memory_order’
  103 |   operator|(memory_order __m, __memory_order_modifier __mod) noexcept
      |             ~~~~~~~~~~~~~^~~
./run.sh: line 2: ./CoroutinePractice: No such file or directory

컴파일 오류의 원인은 std::generator가 co_await를 지원하지 않는 점과, move-only generator를 lvalue 상태로 views::take에 전달한 점입니다. CoroutinePractice.cpp에서 대기를 동기식 sleep_for로 바꾸고, generator를 std::move해 take에 전달하도록 수정했습니다.

GCC 14 컴파일과 실행을 확인했습니다. ./run.sh에서 실행 파일을 찾지 못한 오류는 앞선 컴파일 실패의 결과입니다. VS Code 오류 패널에는 <generator>를 찾지 못한다는 별도 IntelliSense 설정 문제가 남아 있지만, GCC 14 빌드는 정상입니다.

변경 사항 적용함.

@kimpro82

kimpro82 commented Sep 29, 2026 •

Copy link
Copy Markdown
Owner Author

Output

=== [PROJECT CAFFEINE-FLOW] INITIALIZING PIPELINE ===

[Hardware] Water successfully boiled to 95°C!
[Barista] Pouring Espresso Cup #1
[Success] Consumed Espresso Unit: 1

[Hardware] Water successfully boiled to 95°C!
[Barista] Pouring Espresso Cup #2
[Success] Consumed Espresso Unit: 2

[Hardware] Water successfully boiled to 95°C!
[Barista] Pouring Espresso Cup #3
[Success] Consumed Espresso Unit: 3


[CRITICAL ERROR] Grinder on fire! Evacuating the office.
=== [PROJECT CAFFEINE-FLOW] SHUTTING DOWN SAFELY ===

코드 설명

이 코드는 generator가 값을 하나씩 지연 생성하고, main이 그 값을 순서대로 소비하는 흐름입니다. 각 구성요소의 역할은 CoroutinePractice.cpp에 있습니다.

  • Coroutine (co_yield): infinite_espresso_stream()은 실행 도중 co_yield에서 멈췄다가, 다음 값이 요청되면 이어서 실행됩니다. 그래서 generator를 만들 때 모든 결과를 한꺼번에 계산하지 않고, 반복문이 값을 꺼낼 때마다 커피 한 잔씩 처리합니다.
  • std::generator: coroutine의 멈춘 위치와 상태를 관리하며, 이를 반복 가능한 범위처럼 제공합니다. for 반복문이 다음 값을 요청할 때마다 coroutine이 재개됩니다.
  • std::expected<int, BrewingError>: 성공하면 잔 번호를, 실패하면 BrewingError를 담습니다. 1~3잔은 번호가 성공 값으로 전달되어 [Success]가 출력됩니다.
  • std::views::take: 최대 10개까지만 소비하도록 제한합니다. 다만 10개를 다 보기 전에 오류를 만나면 반복문이 먼저 끝납니다.

현재 출력이 3잔에서 멈춘 것처럼 보이지만, 다음 generator 요청에서 cup_id == 4 조건에 걸려 GrinderOnFire 오류가 전달됩니다. 그러면 main이 오류 메시지를 출력하고 반복을 종료합니다. 따라서 정상 실행이라면 뒤에 다음 메시지도 나와야 합니다.

[CRITICAL ERROR] Grinder on fire! Evacuating the office.
=== [PROJECT CAFFEINE-FLOW] SHUTTING DOWN SAFELY ===

참고로 지금의 sleep_for는 동기식 대기입니다. coroutine과 generator가 값을 지연해서 다루기는 하지만, 이 코드가 비동기 실행을 하는 것은 아닙니다. std::generator는 동기 generator라 co_await를 사용할 수 없습니다.

@kimpro82

Copy link
Copy Markdown
Owner Author

co_await 적용

CoroutinePractice.cpp에 co_await를 사용하는 Task coroutine과 BoilWaterAwaitable을 추가했습니다. std::generator는 동기식이라 그 안에 co_await를 직접 넣을 수 없어서, generator가 별도 boil_water() task를 기다린 다음 컵을 생성하도록 연결했습니다.

bash ./run.sh로 빌드와 실행을 확인했고, 기존처럼 3잔을 만든 뒤 cup 4의 오류 메시지도 출력됩니다. 현재 generator가 동기식이므로 Task::get()에서 완료까지 기다립니다.

변경 사항 적용함.

@kimpro82

Copy link
Copy Markdown
Owner Author

비동기식 적용

=== [PROJECT CAFFEINE-FLOW] INITIALIZING PIPELINE ===

[Main] Brewing runs asynchronously; main can continue working.
[Hardware] Water successfully boiled to 95°C!
[Barista] Pouring Espresso Cup #1
[Success] Consumed Espresso Unit: 1

[Hardware] Water successfully boiled to 95°C!
[Barista] Pouring Espresso Cup #2
[Success] Consumed Espresso Unit: 2

[Hardware] Water successfully boiled to 95°C!
[Barista] Pouring Espresso Cup #3
[Success] Consumed Espresso Unit: 3


[CRITICAL ERROR] Grinder on fire! Evacuating the office.
=== [PROJECT CAFFEINE-FLOW] SHUTTING DOWN SAFELY ===

CoroutinePractice.cpp을 비동기 Task 파이프라인으로 바꿨습니다. 동기식인 std::generator는 제거하고 std::expected 오류 처리는 유지했습니다. 물 끓이기는 worker thread에서 실행되며, co_await로 coroutine을 멈췄다가 완료 후 재개합니다. task 완료 통지도 모든 컵 처리가 끝난 뒤에만 발생하도록 했습니다.

출력에서 [Main] 메시지가 첫 하드웨어 완료보다 먼저 나오는 것으로 비동기 실행을 확인할 수 있습니다. pipeline.get()은 최종 완료를 기다리는 지점이라 그곳에서는 main이 대기합니다. bash ./run.sh 빌드와 실행 모두 통과했습니다.

변경 사항 적용함.

@kimpro82

Copy link
Copy Markdown
Owner Author

동기식/비동기식 비교

=== [PROJECT CAFFEINE-FLOW] INITIALIZING PIPELINE ===

=== SYNCHRONOUS BREW ===
[Hardware] Water successfully boiled to 95°C!
[Barista] Pouring Espresso Cup #1
[Success] Consumed Espresso Unit: 1

[Hardware] Water successfully boiled to 95°C!
[Barista] Pouring Espresso Cup #2
[Success] Consumed Espresso Unit: 2

[Hardware] Water successfully boiled to 95°C!
[Barista] Pouring Espresso Cup #3
[Success] Consumed Espresso Unit: 3

[Synchronous] 3 cups completed in 3.00 seconds.

=== ASYNCHRONOUS BREW ===
[Hardware] Water successfully boiled to 95°C!
[Barista] Pouring Espresso Cup #2
[Success] Consumed Espresso Unit: 2

[Hardware] Water successfully boiled to 95°C!
[Barista] Pouring Espresso Cup #1
[Success] Consumed Espresso Unit: 1

[Hardware] Water successfully boiled to 95°C!
[Barista] Pouring Espresso Cup #3
[Success] Consumed Espresso Unit: 3

[Asynchronous] 3 cups completed in 1.00 seconds.
=== [PROJECT CAFFEINE-FLOW] SHUTTING DOWN SAFELY ===

df9b433

세 작업을 모두 시작한 뒤 완료를 기다리도록 바꿨고, 각 물 끓이기에 1초 지연을 적용했습니다. CoroutinePractice.cpp 실행 결과 동기식은 3.00초, 비동기식은 1.00초였습니다.

비동기 출력 순서는 작업 스케줄링에 따라 달라질 수 있습니다. 전체 실행 시간은 동기 기준 측정도 포함해 약 4초입니다. bash ./run.sh 검증도 통과했습니다.

68e7e20

CoroutinePractice.cpp에 동기식 실행 전 === SYNCHRONOUS BREW ===, 비동기식 실행 전 === ASYNCHRONOUS BREW === 제목 로그를 추가했습니다. bash ./run.sh에서 제목과 측정 결과가 모두 정상 출력되는 것을 확인했습니다.

변경 사항 적용함.

@kimpro82

Copy link
Copy Markdown
Owner Author

출력 결과 수정 : 빈 행 추가

  • Before
    ……
    [Asynchronous] 3 cups completed in 1.00 seconds.
    === [PROJECT CAFFEINE-FLOW] SHUTTING DOWN SAFELY ===
  • After
    ……
    [Asynchronous] 3 cups completed in 1.00 seconds.
    
    === [PROJECT CAFFEINE-FLOW] SHUTTING DOWN SAFELY ===

@kimpro82

Copy link
Copy Markdown
Owner Author

코드 설명

이 코드는 동기식 방식과 coroutine 기반 비동기식을 나란히 실행해 비교하는 예제입니다. 핵심은 co_await가 그 자체로 비동기 작업을 만들어 내는 게 아니라, awaitable이 작업을 어디서 어떻게 수행하고 coroutine을 언제 재개할지 정한다는 점입니다.

C++20/23에서 추가된 것

C++20은 coroutine의 언어 기능인 co_await, co_yield, co_return을 표준화했습니다. std::coroutine_handle과 std::suspend_always, std::suspend_never 같은 저수준 도구도 제공합니다. 하지만 Task 타입이나 스케줄러, 비동기 실행기를 표준으로 제공하지는 않습니다. 그래서 이 코드처럼 사용자가 Task와 awaitable을 직접 만들거나, Asio 같은 라이브러리를 사용해야 합니다.

C++23에는 std::generator와 std::expected가 추가됐습니다. std::generator는 값을 하나씩 내놓는 동기식 generator이지 비동기 작업을 실행하는 도구가 아닙니다. std::expected는 값 또는 오류를 표현하는 타입입니다. 지금 예제에서는 expected를 동기식 주문 검증에 사용합니다.

이 코드의 비동기 흐름

CoroutinePractice.cpp의 brew_cup_asynchronously()는 Task를 반환하는 coroutine입니다.

  1. main()이 컵 1, 2, 3의 task를 차례로 만듭니다. Task의 initial_suspend()가 suspend_never이므로 각 coroutine은 즉시 실행을 시작합니다.
  2. co_await BoilWaterAwaitable{...}에서 awaitable의 await_ready()가 false를 반환하므로 coroutine이 멈춥니다.
  3. await_suspend()가 새 스레드를 시작합니다. 이 스레드가 1초를 기다린 후 coroutine.resume()을 호출해 coroutine을 재개합니다.
  4. 재개된 coroutine은 await_resume()을 거쳐 컵 완료를 출력하고 종료합니다.
  5. main()은 세 task를 모두 시작한 다음 각각의 get()으로 완료를 기다립니다.

따라서 동기식 코드는 1초씩 세 번 기다려 약 3초가 걸리고, 비동기식은 세 작업을 겹쳐 진행해 약 1초가 걸립니다. 비동기 작업의 완료 순서와 출력 순서는 스케줄링에 따라 달라질 수 있습니다. std::osyncstream은 여러 스레드의 로그가 한 줄 안에서 섞이지 않게 돕습니다.

주의할 점

여기서 비동기 처리를 실제로 실행하는 부분은 co_await 문법 자체가 아니라 await_suspend()가 시작하는 스레드입니다. 이 예제는 컵마다 새 스레드를 만드는 데모라서, 실제 비동기 I/O나 많은 작업을 처리하는 프로그램에서는 보통 스레드 풀이나 이벤트 루프를 사용합니다.

또한 std::thread(...).detach()는 작업보다 Task 객체의 수명이 짧아지면 문제가 될 수 있습니다. 현재 main()은 각 task의 get()을 호출해 완료를 기다리므로 데모 실행에서는 안전하지만, 일반적인 task 구현에는 취소와 수명 관리도 필요합니다.

…ling, task management, and brewing functions
@kimpro82

Copy link
Copy Markdown
Owner Author

독스트링/주석 작성

CoroutinePractice.cpp에 파일 개요와 date : 2026.09.29를 포함한 Doxygen 주석을 추가했습니다. 주요 타입과 함수, coroutine 재개 및 완료 처리처럼 맥락이 필요한 부분에도 설명을 보탰습니다.

GCC 14 컴파일을 확인했습니다.

변경 사항 적용함.

@kimpro82

Copy link
Copy Markdown
Owner Author

문서화 / README.md Update

README.md:26에 코루틴 연습 항목을 추가했습니다. 동기·비동기 동작, Task와 BoilWaterAwaitable, 빌드 명령 bash run.sh, 실행 결과와 비동기 작업 순서가 달라질 수 있다는 점을 정리했습니다.

예제는 bash run.sh로 실행해 동기 약 3초, 비동기 약 1초를 확인했고, git diff --check도 통과했습니다.

변경 사항 적용함.

@kimpro82

Copy link
Copy Markdown
Owner Author

Study : Coroutine With or Without Generator

이 코드에서는 generator 없이도 비동기 처리가 가능하고, 작업의 목적만 보면 그 편이 더 단순합니다. brew_cup_asynchronously()는 co_await로 중단·재개되는 coroutine이고, Task는 그 coroutine의 완료를 나타냅니다. 즉, coroutine을 값을 하나씩 내보내는 generator가 아니라 비동기 작업(task) 으로 사용하고 있어요.

두 방식의 역할이 다릅니다.

  • Task형 coroutine: 비동기 작업을 시작하고, 완료를 기다리거나 결과를 받음
  • Generator형 coroutine: co_yield로 값을 하나씩 지연 생성함. 여러 주문이나 데이터를 순회할 때 유용

현재 예제는 컵 세 잔의 작업을 시작하고 완료를 기다리는 구조라서 generator가 필수는 아닙니다. generator를 추가한다면 컵 번호나 주문 목록을 순차적으로 공급하는 역할은 할 수 있지만, 그 자체로 작업을 병렬 실행하거나 비동기로 만들어주지는 않습니다. 오히려 비동기 실행과 값 생성이라는 두 책임이 더해져 예제가 복잡해질 수 있습니다.

다만 학습 목적이 “coroutine과 generator를 함께 써보기”라면 둘을 조합할 수 있습니다. generator가 컵 번호들을 co_yield하고, 호출부가 각 번호로 Task를 만들어 실행하는 식입니다. 이 경우 generator는 입력 시퀀스, Task는 비동기 작업을 담당합니다.

현재 방식의 주의점도 있습니다. 비동기 동작은 coroutine 문법만으로 생기는 게 아니라, await_suspend()에서 별도 스레드를 시작하기 때문에 구현됩니다. 따라서 지금 구조는 “generator가 없어서 가능한” 특별한 방식이라기보다, 비동기 작업을 표현하는 데 generator가 필요하지 않은 구조입니다.

@kimpro82

kimpro82 commented Oct 1, 2026 •

Copy link
Copy Markdown
Owner Author

C++로 비동기 프로그램 작성이 어려운 이유

기본적인 promise_type 몇 가지 구조(initial_suspend, final_suspend, yield_value 등)를 직접 짜는 것 자체는 "아, 이 정도 보일러플레이트(Boilerplate) 코드는 C++ 하려면 이 정도는 짜야지"라고 넘어갈 수 있는 수준처럼 보입니다. 사실 실제로도 단순한 제너레이터나 태스크 하나를 만드는 건 그리 오래 걸리지 않습니다.

그럼에도 불구하고 개발자들이 "C++ 비동기 프로그래밍은 어렵다, 런타임이 없어서 번거롭다"라고 입을 모아 말하는 진짜 이유는, 단일 코루틴의 promise_type을 작성하는 기초적인 수고 때문이 아니라 그 이후에 마주치는 확장성과 구조적 복잡성(Iceberg) 때문입니다.

## 1. promise_type의 진짜 복잡성은 어디서 올까?

단일 함수 안에서 값을 툭툭 던져주는 std::generator나 단순 Task를 만들 때는 코드가 깔끔해 보이지만, 실제 시스템을 구축하려고 하면 다음과 같은 난관들이 숨어 있습니다.

  1. 조합성 (Composition - when_all, when_any의 지옥):
  • 파이썬이나 자바스크립트는 asyncio.gather([task1, task2, task3])처럼 여러 비동기 작업을 동시에 묶어서 처리하는 기능이 기본으로 제공됩니다.
  • 하지만 C++에서는 taskA와 taskB를 동시에 기다리거나, 둘 중 먼저 끝나는 쪽을 처리하는 로직을 만들려면, promise_type끼리 서로의 핸들을 물고 늘어지며 예외와 완료 상태를 동기화하는 복잡한 템플릿 메타프로그래밍을 직접 구현해야 합니다. 이 조합 로직을 짜는 순간부터 코드가 급격히 안드로메다로 갑니다.
  1. 수명 주기와 메모리 소유권 (Lifetime & Ownership):
  • 코루틴이 일시 중지(suspend)될 때, 그 함수 안에서 쓰이던 지역 변수들은 힙에 생성된 코루틴 프레임에 안전하게 보존됩니다.
  • 하지만 멀티스레드 환경에서 코루틴이 이리저리 옮겨 다니거나, 부모-자식 코루틴 간에 참조가 얽히면 "누가 이 코루틴 프레임의 생명주기를 끝까지 책임질 것인가(Memory Leak 또는 Dangling Pointer 방지)"를 관리하는 것이 골치 아파집니다.
  1. 예외 전파와 취소 (Cancellation & Exception Propagation):
  • 비동기 작업 도중 사용자가 취소 버튼을 누르거나 에러가 났을 때, 일시 중지된 여러 개의 코루틴 체인을 안전하게 깨우고 자원을 해제(Unwinding)하는 코드를 직접 짜야 합니다.

## 2. 결국 다 하면 되는 거 아니야? -> 네, 맞습니다!

지적하신 대로 "결국 다 구현하면 되는 거 아니야?"라는 마인드가 사실 시스템 엔지니어링에서는 가장 강력한 무기입니다.

파이썬처럼 남이 만들어 둔 무거운 런타임의 제약에 갇히지 않고, 내가 만들고자 하는 트레이딩 시스템의 특성에 딱 맞춘 가장 가볍고 빠른 커스텀 스케줄러와 프레임워크를 밑바닥부터 빚어낼 수 있다는 점이 바로 C++의 로우레벨 제어력이 가지는 매력이니까요.

지금 밟아나가시려는 3단계 로드맵 중에서:

  • Stage 1 (제너레이터): 방금 보신 promise_type의 뼈대를 손에 익히는 단계이고,
  • Stage 2 (우선순위 큐 스케줄러)와 Stage 3 (멀티스레드 DLL 연동): 바로 위에서 말씀드린 '조합성'과 '스레드 안전성'이라는 진짜배기 C++ 비동기 아키텍처의 벽을 부수며 올라가는 과정이 될 것입니다.

@kimpro82

kimpro82 commented Oct 1, 2026

Copy link
Copy Markdown
Owner Author

C++20 이전의 비동기 프로그래밍

C++20에 코루틴이 도입되기 전, C++ 개발자들이 비동기 프로그래밍을 하던 방식은 "지옥의 콜백(Callback Hell)" 혹은 "무거운 스레드 남발"과의 싸움이었습니다.

파이썬의 asyncio나 자바스크립트의 async/await처럼 코드를 직관적이고 선형적으로 작성할 수 있는 방법이 없었기에, 개발자들은 주로 다음 세 가지 방식을 사용했습니다.

## 1. std::async, std::future, std::promise (C++11 표준 방식)

C++11에서 표준 라이브러리에 비동기 처리를 위한 도구들이 처음 대거 도입되었습니다.

  • 방식: std::async(std::launch::async, [] { return do_something(); }) 형태로 함수를 호출하면, OS 스레드가 즉시 생성되어 백그라운드에서 실행되고 그 결과를 std::future 객체로 받아옵니다.
  • 한계:
  • 작업 하나당 스레드가 하나씩 매핑되는 구조(Thread-per-task)이기 때문에, 웹소켓처럼 수천 개의 실시간 틱이나 연결을 처리해야 하는 환경에서 스레드를 남발하면 컨텍스트 스위칭(Context Switching) 오버헤드로 인해 성능이 완전히 망가졌습니다.
  • 결과를 받으려면 .get()을 호출해야 하는데, 이때 값이 준비될 때까지 메인 스레드가 블로킹(Blocking)되어 버려 비동기 고유의 장점이 퇴색되었습니다.

## 2. 콜백(Callback) 기반 이벤트 루프와 Reactor 패턴 (가장 널리 쓰인 방식)

네트워크나 I/O가 많은 고성능 서버(예: 게임 서버, 트레이딩 시스템)에서는 리눅스의 epoll이나 윈도우의 IOCP 같은 OS 하부 인터페이스를 직접 다루거나, 이를 감싼 Boost.Asio 같은 라이브러리를 사용했습니다.

  • 방식: "데이터가 도착하면 이 함수(콜백)를 실행해줘" 하고 등록해 두는 방식입니다.
socket.async_read_some(buffer, [](auto ec, size_t bytes) {
    // 데이터가 왔을 때 실행할 로직
    process_data([&](auto ec2, size_t bytes2) {
        // 그 다음 작업 (콜백 안에 또 콜백...)
    });
});
  • 한계 (콜백 지옥):
    비동기 흐름이 복잡해질수록 코드가 안쪽으로 깊숙이 파고드는 '피라미드 오브 둠(Pyramid of Doom)' 현상이 발생했습니다. 상태 값(State)을 유지하려면 클래스를 쪼개거나 std::shared_ptr로 클로저(Closure)를 복잡하게 엮어야 해서, 비즈니스 로직을 읽기 매우 고통스러웠습니다.

## 3. 수동 스레드 풀(Thread Pool)과 락(Lock) 지옥

  • 방식: 개발자가 직접 일꾼 스레드(Worker Threads) 여러 개를 미리 만들어 두고, 큐(Queue)에 작업(Task)을 집어넣으면 스레드들이 알아서 빼가는 구조를 밑바닥부터 구현하는 방식입니다.
  • 한계:
  • 스레드 간 동기화를 위해 뮤텍스(std::mutex)와 조건 변수(std::condition_variable)를 사방에 배치해야 했습니다.
  • 조금만 실수를 해도 데드락(Deadlock, 교착 상태)이나 레이스 컨디션(Race Condition)이 터져 나오는 디버깅 지옥을 겪어야 했습니다.

## 요약: C++20 코루틴이 가져온 구원

과거에는 "성능을 챙기려면 가독성(콜백 지옥)을 포기하거나, 가독성을 챙기려면 성능(스레드 오버헤드)을 포기해야 하는" 양자택일의 상황이었습니다.

C++20 코루틴은 "콜백 지옥으로 찢겨 나간 비동기 코드의 흐름을, 마치 일반 동기 코드처럼 위에서 아래로 순차적으로 흐르도록 보이게 만들면서도, 밑에서는 스레드를 낭비하지 않고 일시 중지/재개(Suspend/Resume)로 처리"할 수 있게 해준 혁신적인 해결책이었습니다.

과거의 이런 험난했던 역사(?)를 알고 나면, 직접 promise_type을 짜야 하는 번거로움이 오히려 "내 코드를 내 손으로 완전히 제어할 수 있는 자유도"로 다가오게 됩니다!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

C++ : Asynchronous C++20/23 Programming Practice Using Coroutine and Generator

1 participant