C++20부터 지원
Coroutines한번 진입하고 한번 리턴하는 일반함수와 달리
코루틴은 여러 개의 진입점(resume)과 여러 개의 종단점(suspend)을 가진다.
return이 원래 함수의 종료하고 스택 메모리에 할당된 모든 리소스를 해제하지만,
코루틴에서는 다시 재개하기 위한 코루틴 스테이트(coroutine state)를 저장하고 제어권을 호출자에게 다시 넘긴다.
coroutine은 일반 함수와 달리 기본적으로 스택이 아닌 힙 메모리 영역을 사용하게 됨
제약 사항- 코루틴은 Variadic arguments, 일반 return문, auto와 placeholder 리턴 타입 사용 불가능
- constexpr함수와 생성자, 소멸자, 메인 함수를 코루틴으로 사용 불가능
co_await, co_yield, co_return
co_return: 종결
co_await, co_yield: 중단(co_await는 단순 중단, co_yield는 1항 연산자로 전달 값이 있을 때 사용)
coro.resume(): 재개( coroutine_handle<P>::resume() )
함수 내에 co_await, co_yield, co_return, for co_await 키워드 중 하나라도 존재하면 코루틴으로 처리함
gcc(는 아직 지원하지 않음)
c++ -std=c++20
- promise: 코루틴 결과나 예외를 객체를 통해 호출자에게 전달하는 용도(코루틴 내부에서 관리)
- coroutine handle: 코루틴을 resume하거나 코루틴 프레임을 제거할 때 사용(코루틴 외부에서 관리)
- coroutine state: 코루틴 상태를 나타내는 객체(힙 메모리에 할당)
#include <iostream>
#include <coroutine>
class typeyield{
public:
struct promise_type{
typeyield get_return_object(void){
return typeyield{std::coroutine_handle<promise_type>::from_promise(*this)};
}
auto initial_suspend(void){ return std::suspend_always{}; }
auto return_void(void){ return std::suspend_never{}; }
auto final_suspend(void){ return std::suspend_always{}; }
void unhandled_exception(void){ std::exit(1); }
};
std::coroutine_handle<promise_type> co_handler;
typeyield(std::coroutine_handle<promise_type> handler): co_handler(handler){}
~typeyield(void){
if((bool)co_handler==true) co_handler.destroy();
}
};
typeyield coroutineFunction(void){
std::cout<<"Begin Coroutine"<<std::endl;
co_await std::suspend_always{};
std::cout<<"End Coroutine"<<std::endl;
}
int main(void){
typeyield ty=coroutineFunction();
std::cout<<"\tBefore Call"<<std::endl;
ty.co_handler.resume();
std::cout<<"\tAfter Call"<<std::endl;
ty.co_handler.resume();
}
코루틴의 장점(compare with thread)1. 공유 자원에 대해서 동기화 관련 문제가 발생하지 않음
쓰레드와 달리 서로 제어권을 변경하며 공유하기 때문에 동기화 관련 문제가 발생하지 않는다.
2. 쓰레드와 비교하여 적은 비용으로 처리 가능
쓰레드의 생성과 삭제 그리고 컨텍스트 스위칭 비용에 비해 적은 비용이 발생하여 성능이 좋다.
(I/O처리와 같이 동시적으로 처리해야 하는 경우에는 쓰레드가 더 뛰어남)
python coroutine
def coroutineFn():
print("abc")
yield
print("def")
yield
print("ghi")
yield
def main():
obj=coroutineFn()
print("123")
next(obj)
print("456")
next(obj)
print("789")
if __name__=="__main__":
main()
C++ coroutine
#include <coroutine>
#include <iostream>
struct CoType{
struct promise_type{
std::suspend_always initia_suspend(void){
return {};
}
std::suspend_never final_suspend(void) noexcept{
return {};
}
CoType get_return_object(void){
return CoType{std::coroutine_handle<promise_type>::from_promise(*this)};
}
void return_void(void){}
void unhandled_exception(void){
std::exit(1);
}
};
std::coroutine_handle<promise_type> coro;
CoType(std::coroutine_handler<promise_Type> h): coro(h){}
~CoType(void){
if(coro) coro.destroy();
}
void next(void){
coro.resume();
}
};
CoType coroutinFn(void){
std::cout<<"abc"<<std::endl;
co_await std::suspend_always{};
std::cout<<"def"<<std::endl;
co_await std::suspend_always{};
std::cout<<"ghi"<<std::endl;
co_await std::suspend_always{};
}
int main(void){
CoType coObj=coroutineFn();
std::cout<<"123"<<std::endl;
coObj.next();
std::cout<<"456"<<std::endl;
coObj.next();
std::cout<<"789"<<std::endl;
coObj.next();
}