본문으로 건너뛰기
Previous
Next
C++ Coroutines | Asynchronous Programming in C++20

C++ Coroutines | Asynchronous Programming in C++20

C++ Coroutines | Asynchronous Programming in C++20

이 글의 핵심

C++20 coroutines: coroutine_handle, promise_type, co_await, generators, Task patterns, and compiler support for async-style control flow.

Coroutine basics

#include <coroutine>
#include <iostream>
using namespace std;
struct Task {
    struct promise_type {
        Task get_return_object() {
            return Task{handle_type::from_promise(*this)};
        }
        
        suspend_never initial_suspend() { return {}; }
        suspend_never final_suspend() noexcept { return {}; }
        void return_void() {}
        void unhandled_exception() {}
    };
    
    using handle_type = coroutine_handle<promise_type>;
    handle_type coro;
    
    Task(handle_type h) : coro(h) {}
    ~Task() { if (coro) coro.destroy(); }
};
Task simpleCoroutine() {
    cout << "Coroutine start" << endl;
    co_return;
}
int main() {
    simpleCoroutine();
}

Generators

#include <coroutine>
#include <iostream>
using namespace std;
template<typename T>
struct Generator {
    struct promise_type {
        T current_value;
        
        Generator get_return_object() {
            return Generator{handle_type::from_promise(*this)};
        }
        
        suspend_always initial_suspend() { return {}; }
        suspend_always final_suspend() noexcept { return {}; }
        
        suspend_always yield_value(T value) {
            current_value = value;
            return {};
        }
        
        void return_void() {}
        void unhandled_exception() {}
    };
    
    using handle_type = coroutine_handle<promise_type>;
    handle_type coro;
    
    Generator(handle_type h) : coro(h) {}
    ~Generator() { if (coro) coro.destroy(); }
    
    bool move_next() {
        coro.resume();
        return !coro.done();
    }
    
    T current_value() {
        return coro.promise().current_value;
    }
};
Generator<int> counter(int max) {
    for (int i = 0; i < max; i++) {
        co_yield i;
    }
}
int main() {
    auto gen = counter(5);
    
    while (gen.move_next()) {
        cout << gen.current_value() << " ";  // 0 1 2 3 4
    }
}

co_await

struct Awaitable {
    bool await_ready() { return false; }
    void await_suspend(coroutine_handle<>) {}
    void await_resume() {}
};
Task asyncFunction() {
    cout << "Start" << endl;
    co_await Awaitable{};
    cout << "Resumed" << endl;
}

Practical examples

Example 1: Fibonacci generator

Generator<int> fibonacci(int n) {
    int a = 0, b = 1;
    
    for (int i = 0; i < n; i++) {
        co_yield a;
        int temp = a;
        a = b;
        b = temp + b;
    }
}
int main() {
    auto fib = fibonacci(10);
    
    while (fib.move_next()) {
        cout << fib.current_value() << " ";
    }
}

Example 2: Range generator

Generator<int> range(int start, int end, int step = 1) {
    for (int i = start; i < end; i += step) {
        co_yield i;
    }
}
int main() {
    for (auto gen = range(0, 10, 2); gen.move_next();) {
        cout << gen.current_value() << " ";  // 0 2 4 6 8
    }
}

Example 3: Read file lines

#include <fstream>
Generator<string> readLines(const string& filename) {
    ifstream file(filename);
    string line;
    
    while (getline(file, line)) {
        co_yield line;
    }
}
int main() {
    auto lines = readLines("input.txt");
    
    while (lines.move_next()) {
        cout << lines.current_value() << endl;
    }
}

Example 4: Async timer

#include <chrono>
#include <thread>
struct Timer {
    chrono::milliseconds duration;
    
    bool await_ready() { return false; }
    
    void await_suspend(coroutine_handle<> h) {
        thread([h, d = duration]() {
            this_thread::sleep_for(d);
            h.resume();
        }).detach();
    }
    
    void await_resume() {}
};
Task asyncTask() {
    cout << "Start" << endl;
    
    co_await Timer{chrono::seconds(1)};
    cout << "After 1 second" << endl;
    
    co_await Timer{chrono::seconds(1)};
    cout << "After 2 seconds total" << endl;
}

Coroutines vs threads

// Thread (heavier)
void threadExample() {
    thread t([] {
        // work
    });
    t.join();
}
// Coroutine (lighter)
Task coroutineExample() {
    co_await someOperation();
    // work
}

Differences:

  • Thread: OS resource, context-switch cost
  • Coroutine: user space, lightweight suspend

Common problems

Problem 1: Missing promise_type

// Error without promise_type
Task myCoroutine() {
    co_return;
}
// Define promise_type inside Task
struct Task {
    struct promise_type {
        // ...
    };
};

Problem 2: Forgetting to destroy the handle

// Leak if destructor omits destroy
Generator<int> gen = counter(10);
// Destructor should call coro.destroy()
~Generator() {
    if (coro) coro.destroy();
}

Problem 3: Local variable lifetime

Task bodies store locals in the coroutine frame; still avoid dangling references to outer stack objects across suspension points without copying.

Coroutine state

The following example demonstrates the concept in cpp:

coroutine_handle<> h = ...;
h.resume();
h.done();
h.destroy();
h.promise();

FAQ

Q1: When should I use coroutines?

A: Async I/O, generators, state machines, cooperative multitasking.

Q2: Coroutines vs async/await in other languages?

A: C++ coroutines are low-level; libraries like cppcoro add ergonomics.

Q3: Performance?

A: Much lighter than OS threads; many concurrent coroutines are feasible.

Q4: Compiler support?

A: GCC 10+, Clang 14+, MSVC 2019+.

Q5: Debugging?

A: Harder than plain functions; add logging; debugger support is limited.

Q6: Learning resources?

A: cppreference.com, “C++20: The Complete Guide”, cppcoro, Lewis Baker blog.

Practical tips

Debugging

  • Check compiler warnings first
  • Reproduce with a small test case

Performance

  • Profile before optimizing

Code review

  • Follow team conventions

Production checklist

Before coding

  • Best fit for the problem?
  • Maintainable by the team?
  • Meets performance needs?

While coding

  • Warnings cleared?
  • Edge cases handled?

At review

  • Intent clear?
  • Tests sufficient?

Keywords

C++, coroutine, C++20, async, asynchronous


같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.


이 글에서 다루는 키워드 (관련 검색어)

C++, coroutine, C++20, async, asynchronous 등으로 검색하시면 이 글이 도움이 됩니다.