C ++ 20에서 코 루틴은 무엇입니까?


104

코 루틴이란? ?

"Parallelism2"또는 / 및 "Concurrency2"(아래 이미지 참조)와 어떤 점에서 다른가요?

아래 이미지는 ISOCPP에서 가져온 것입니다.

https://isocpp.org/files/img/wg21-timeline-2017-03.png

여기에 이미지 설명 입력


3
" 코 루틴 의 개념이 병렬성동시성 과 어떻게 다른 가요?" - en.wikipedia.org/wiki/Coroutine
벤 보이트


3
코 루틴에 대한 매우 훌륭하고 따라하기 쉬운 소개는 James McNellis의 "Introduction to C ++ Coroutines"(Cppcon2016)입니다.
philsumuru

2
마지막으로는 커버에 좋은 것 "어떻게하다 코 루틴 코 루틴 및 재개 기능의 다른 언어 '구현에서 다른 C에서 + +를?" (위에 링크 된 위키피디아 기사는 언어에 구애받지 않고 다루지 않습니다.)
Ben Voigt

1
또 누가이 "C ++ 20의 격리"를 읽었습니까?
Sahib Yar

답변:


198

추상적 인 수준에서 코 루틴은 실행 스레드를 갖는 아이디어에서 실행 상태를 갖는 아이디어를 분리합니다.

SIMD (단일 명령어 다중 데이터)에는 여러 "실행 스레드"가 있지만 실행 상태는 하나뿐입니다 (여러 데이터에서만 작동 함). 아마도 병렬 알고리즘은 하나의 "프로그램"이 다른 데이터에서 실행된다는 점에서 이와 비슷합니다.

스레딩에는 여러 "실행 스레드"와 여러 실행 상태가 있습니다. 둘 이상의 프로그램과 둘 이상의 실행 스레드가 있습니다.

코 루틴은 여러 실행 상태를 갖지만 실행 스레드를 소유하지 않습니다. 프로그램이 있고 프로그램에 상태가 있지만 실행 스레드가 없습니다.


코 루틴의 가장 쉬운 예는 다른 언어의 생성자 또는 열거 형입니다.

의사 코드에서 :

function Generator() {
  for (i = 0 to 100)
    produce i
}

Generator라고하며 그것을 처음 호출이 반환됩니다 0. 상태가 기억되고 (코 루틴의 구현에 따라 상태가 얼마나 달라지는 지) 다음에 호출 할 때 중단 된 지점에서 계속됩니다. 따라서 다음에 1을 반환합니다. 그런 다음 2.

마지막으로 루프의 끝에 도달하고 함수의 끝에서 떨어집니다. 코 루틴이 완성되었습니다. (여기서 일어나는 일은 우리가 말하는 언어에 따라 다릅니다. 파이썬에서는 예외가 발생합니다).

코 루틴은이 기능을 C ++로 가져옵니다.

코 루틴에는 두 종류가 있습니다. 스택 및 스택리스.

스택리스 코 루틴은 상태와 실행 위치에 로컬 변수 만 저장합니다.

스택 형 코 루틴은 스레드와 같은 전체 스택을 저장합니다.

스택리스 코 루틴은 매우 가볍습니다. 내가 읽은 마지막 제안은 기본적으로 함수를 람다와 같은 것으로 다시 작성하는 것과 관련이 있습니다. 모든 지역 변수는 객체의 상태가되며 레이블은 코 루틴이 중간 결과를 "생성"하는 위치로 /에서 점프하는 데 사용됩니다.

코 루틴은 협동 멀티 스레딩과 비슷하기 때문에 값을 생성하는 과정을 "수익"이라고합니다. 실행 지점을 호출자에게 다시 양보합니다.

Boost에는 스택 형 코 루틴이 구현되어 있습니다. 당신을 위해 항복하는 함수를 호출 할 수 있습니다. 스택 형 코 루틴은 더 강력하지만 더 비쌉니다.


코 루틴에는 단순한 생성기보다 더 많은 것이 있습니다. 유용한 방식으로 코 루틴을 구성 할 수있는 코 루틴에서 코 루틴을 기다릴 수 있습니다.

if, 루프 및 함수 호출과 같은 코 루틴은보다 자연스러운 방식으로 특정 유용한 패턴 (상태 머신과 같은)을 표현할 수있는 또 다른 종류의 "구조화 된 goto"입니다.


C ++에서 코 루틴의 특정 구현은 약간 흥미 롭습니다.

가장 기본적인 수준에서 C ++ :에 몇 가지 키워드 co_return co_await co_yield와 함께 작동하는 일부 라이브러리 유형을 추가합니다.

함수는 본문에 하나를 포함하여 코 루틴이됩니다. 따라서 선언에서 그들은 함수와 구별 할 수 없습니다.

이 세 키워드 중 하나가 함수 본문에 사용되면 반환 유형 및 인수에 대한 표준 검사가 수행되고 함수가 코 루틴으로 변환됩니다. 이 검사는 함수가 일시 중단 될 때 함수 상태를 저장할 위치를 컴파일러에 알려줍니다.

가장 간단한 코 루틴은 생성기입니다.

generator<int> get_integers( int start=0, int step=1 ) {
  for (int current=start; true; current+= step)
    co_yield current;
}

co_yield함수 실행을 일시 중단하고 해당 상태를에 저장 generator<int>한 다음를 current통해의 값을 반환 합니다 generator<int>.

반환 된 정수를 반복 할 수 있습니다.

co_await한편 한 코 루틴을 다른 코 루틴에 스플 라이스 할 수 있습니다. 하나의 코 루틴에 있고 진행하기 전에 기다릴 수있는 일 (종종 코 루틴)의 결과가 필요한 경우, 그 결과를 co_await수행합니다. 준비가 되었으면 즉시 진행하십시오. 그렇지 않은 경우 대기중인 대기 가능 항목이 준비 될 때까지 일시 중지합니다.

std::future<std::expected<std::string>> load_data( std::string resource )
{
  auto handle = co_await open_resouce(resource);
  while( auto line = co_await read_line(handle)) {
    if (std::optional<std::string> r = parse_data_from_line( line ))
       co_return *r;
  }
  co_return std::unexpected( resource_lacks_data(resource) );
}

load_datastd::future명명 된 리소스가 열릴 때 를 생성하고 요청 된 데이터를 찾은 지점까지 파싱 하는 코 루틴입니다 .

open_resourceread_lines는 아마도 파일을 열고 파일에서 행을 읽는 비동기 코 루틴 일 것입니다. 는 co_await의 중단과 준비 상태를 연결 load_data진행 상황에.

C ++ 코 루틴은 사용자 공간 유형 위에 최소한의 언어 기능 세트로 구현 되었기 때문에 이보다 훨씬 더 유연합니다. 사용자 공간 유형은 효과적으로 의미co_return co_awaitco_yield 의미를 정의합니다. 사람들이 모나 딕 옵 셔널 표현식을 구현하는 데 사용하는 것을 보았습니다. 그러면 co_await빈 옵 셔널이 자동으로 빈 상태를 외부 옵 셔널로 전파합니다.

modified_optional<int> add( modified_optional<int> a, modified_optional<int> b ) {
  return (co_await a) + (co_await b);
}

대신에

std::optional<int> add( std::optional<int> a, std::optional<int> b ) {
  if (!a) return std::nullopt;
  if (!b) return std::nullopt;
  return *a + *b;
}

26
이것은 내가 읽은 코 루틴이 무엇인지에 대한 가장 명확한 설명 중 하나입니다. 그것들을 SIMD 및 클래식 스레드와 비교하고 구별하는 것은 훌륭한 아이디어였습니다.
Omnifarious

2
추가 옵션 예제를 이해하지 못합니다. std :: optional <int>는 대기 가능한 개체가 아닙니다.
Jive Dadson

1
@mord yes 1 요소를 반환해야합니다. 연마가 필요할 수 있습니다. 둘 이상의 라인을 원한다면 다른 제어 흐름이 필요합니다.
Yakk-Adam Nevraumont

1
@lf 죄송합니다 ;;.
Yakk - 아담 Nevraumont

1
그런 간단한 기능에 대한 @LF 아마도 차이가 없을 것입니다. 그러나 일반적으로 볼 수있는 차이점은 코 루틴은 본문의 진입 / 종료 (실행) 지점을 기억하는 반면 정적 함수는 매번 처음부터 실행을 시작한다는 것입니다. "로컬"데이터의 위치는 내가 생각하기에 무관하다.
avp

21

코 루틴은 여러 개의 return 문이있는 C 함수와 같으며 두 번째 호출시 함수 시작시 실행이 시작되지 않고 이전에 실행 된 반환 후 첫 번째 명령에서 실행됩니다. 이 실행 위치는 코 루틴이 아닌 함수의 스택에있는 모든 자동 변수와 함께 저장됩니다.

Microsoft의 이전 실험적 코 루틴 구현에서는 복사 된 스택을 사용 했으므로 깊은 중첩 함수에서 돌아올 수도 있습니다. 그러나이 버전은 C ++위원회에서 거부되었습니다. 예를 들어 Boosts 파이버 라이브러리를 사용하여이 구현을 얻을 수 있습니다.


1

코 루틴은 다른 루틴이 완료 될 때까지 "대기"하고 일시 중지, 일시 중지, 대기, 루틴이 계속 진행되는 데 필요한 모든 것을 제공 할 수있는 (C ++에서) 함수 여야합니다. C ++ 사람들에게 가장 흥미로운 기능은 코 루틴이 이상적으로 스택 공간을 차지하지 않는다는 것입니다 ... C #은 이미 await 및 yield로 이와 같은 작업을 수행 할 수 있지만 C ++를 다시 빌드해야 할 수 있습니다.

동시성은 관심사가 프로그램이 완료해야하는 작업 인 관심사 분리에 중점을 둡니다. 이러한 우려의 분리는 여러 가지 방법으로 달성 될 수 있습니다. 일반적으로 일종의 위임입니다. 동시성의 개념은 여러 프로세스가 독립적으로 실행될 수 있고 (관심의 분리) '청취자'가 분리 된 관심사에 의해 생성 된 모든 것을 이동해야하는 곳으로 지시한다는 것입니다. 이것은 일종의 비동기 관리에 크게 의존합니다. Aspect 지향 프로그래밍 및 기타를 포함하여 동시성에 대한 많은 접근 방식이 있습니다. C #에는 아주 잘 작동하는 '대리자'연산자가 있습니다.

병렬 처리는 동시성처럼 들리며 관련 될 수 있지만 실제로는 코드의 일부를 실행되고 결과가 다시 수신되는 다른 프로세서로 코드의 일부를 보낼 수있는 소프트웨어와 다소 병렬 방식으로 배열 된 많은 프로세서를 포함하는 물리적 구조입니다. 동 기적으로.


9
동시성과 우려의 분리는 전혀 관련이 없습니다. 코 루틴은 일시 중단 된 루틴에 대한 정보를 제공하는 것이 아니라 재개 가능한 루틴입니다.
Ben Voigt
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.