C++20에 추가된 coroutine은 기존 sub-routine과 다르게 caller 함수와 협력 관계로 동작합니다.

Coroutine 개념 및 특징

structure

  1. co-routine은 호출 중간에 작업을 중단 하고 호출 위치로 복귀 이후, 중단 시점에서 다시 작업 재개가 가능합니다.
  2. Heap에 coroutine의 Stack frame을 보관하여 복귀 시점을 찾는 등의 동작으로 구현 되어 있습니다.
  3. C++20에서는 다른 언어의 co-routine 보다 사용이 다소 복잡하지만, 유연성을 제공합니다.
  4. single thread로 동작 할 수도 있고, multi thread로도 동작 할 수 있습니다.
  5. 신규 헤더 : coroutine
  6. 신규 키워드 : co_await, co_yield

기본 예제

#include <iostream>
#include <coroutine>
#include <Windows.h>

// Coroutine을 사용하기 위한 최소한의 구현 객체 Coroutine framework
template<typename T>
class Generator
{
public:
    struct Promise
    {
private:
        T value;
public:
        T Getvalue() { return value; }
        Generator get_return_object()
        {
            return Generator{ std::coroutine_handle<Promise>::from_promise(*this) };
		}
        
        auto yield_value(int n)
        {
            value = n;
            return std::suspend_always{};
        }
        
        auto initial_suspend() { return std::suspend_always{}; }
        auto return_void() { return std::suspend_never{}; }
        auto final_suspend() { return std::suspend_always{}; }
        auto unhandled_exception() { return exit(1); }
    };
    using promise_type = Promise;
    
    std::coroutine_handle<promise_type> coro;
    
    Generator( std::coroutine_handle<promise_type> c ) : coro(c) {}
    
    ~Generator() { if ( coro ) coro.destroy(); }
};

Generator f()
{
    std::cout << "Run 1" << std::endl;
    
    Sleep(10000);
    //co_await std::suspend_always {}; // 작업 중단 시점 명시, suspend_always -> awaitable object
    co_yiled 10;
    
    std::cout << "Run 2" << std::endl;
    co_yield 20;
    
    std::cout << "Run 3" << std::endl;
}

int main()
{
    Generator<int> g = foo();
    
    std::cout << "main1" << std::endl;
   
   	
    while(true)
    {
         g.coro.resume();
        
        // 코루틴이 작업중인지 확인 가능
        if( g.coro.done() ) break;
    }
    
    std::cout << g.coro.promise().getvalue() << std::endl; // 코루틴과 값 주고 받기
    
    std::cout << "main2" << std::endl;
    g.coro.resume();
    
    std::cout << g.coro.promise().getvalue() << std::endl;
    
    std::cout << "main3" << std::endl;
}

Awatible object (멀티 스레딩)

#include <iostream>
#include <coroutine>
#include <Windows.h>

// Coroutine을 사용하기 위한 최소한의 구현 객체 Coroutine framework
template<typename T>
class Generator
{
public:
    struct Promise
    {
private:
        T value;
public:
        T Getvalue() { return value; }
        Generator get_return_object()
        {
            return Generator{ std::coroutine_handle<Promise>::from_promise(*this) };
		}
        
        auto yield_value(int n)
        {
            value = n;
            return std::suspend_always{};
        }
        
        auto initial_suspend() { return std::suspend_always{}; }
        auto return_void() { return std::suspend_never{}; }
        auto final_suspend() { return std::suspend_always{}; }
        auto unhandled_exception() { return exit(1); }
    };
    using promise_type = Promise;
    
    std::coroutine_handle<promise_type> coro;
    
    Generator( std::coroutine_handle<promise_type> c ) : coro(c) {}
    
    ~Generator() { if ( coro ) coro.destroy(); }
};

struct resume_new_thread
{
    void await_suspend(std::coroutine_handle<> handle)
    {
        std::thread t([handle]() { handle(); });
        t.detach();
    }
    constexpr bool await_read() const noexcept { return false; }
    constexpr bool await_resume() const noexcept { }
}

Generator f()
{
    std::cout << "Run 1 : " << std::this_thread::get_id() << std::endl;
    
	co_wait resume_new_thread{}; //여기에 걸리면 새로운 thread를 만들어라
    
    std::cout << "Run 2 : " << std::this_thread::get_id() << std::endl;
}

int main()
{
    Generator<int> g = foo();
     g.coro.resume(); // resume시 새로운 스레드가 생겨서 Run 2 실행(멀티스레딩)
    
    std::cout << "main" << std::endl;
    
    int n;
    std::cin >> n;

}

더 많은 C++20 관련 정보