C++ コルーチンの例

C Koruchinno Li



コルーチンは、より組織的かつ線形な方法で非同期コードを作成できる言語機能を提供し、構造化された逐次的なアプローチを促進します。これらは、スレッド全体を停止することなく、特定のインスタンスで関数の実行を一時停止および再開するメカニズムを提供します。コルーチンは、ファイルからの読み取りやネットワーク呼び出しの送信など、I/O 操作の待機が必要なタスクを処理する場合に役立ちます。

コルーチンは、関数が値を生成し、後で再開して実行を継続できるジェネレーターの概念に基づいています。コルーチンは、非同期操作を管理するための強力なツールを提供し、コードの全体的な品質を大幅に向上させることができます。

コルーチンの使用法

コルーチンは、最新のプログラミング、特に C++ などの言語で必要となる理由がいくつかあります。コルーチンが有益である主な理由をいくつか示します。







コルーチンは、非同期プログラミングに対する洗練されたソリューションを提供します。これらにより、論理的で理解しやすい、連続的でブロック的なコードを作成することが可能になります。コルーチンは、スレッドをブロックせずに特定の時点で実行を一時停止できるため、他のタスクの並列操作が可能になります。このため、システム リソースがより効率的に使用され、I/O 操作や外部イベントの待機を伴うアプリケーションの応答性が向上します。



これらにより、コードの理解と保守が容易になる可能性があります。コルーチンを使用すると、複雑なコールバック チェーンやステート マシンが不要になるため、より線形で逐次的なスタイルでコードを記述することができます。これにより、コードの構成が改善され、ネストが減り、ロジックが理解しやすくなります。



コルーチンは、同時実行性と並列処理を処理するための構造化された方法を提供します。これらを使用すると、より直感的な構文を使用して、複雑な調整パターンと非同期ワークフローを表現できます。スレッドがブロックされる可能性がある従来のスレッド モデルとは異なり、コルーチンはシステム リソースを解放し、効率的なマルチタスクを可能にします。





C++ でのコルーチンの実装を示すためにいくつかの例を作成してみましょう。

例 1: 基本的なコルーチン

基本的なコルーチンの例を以下に示します。



#include

#include <コルーチン>

構造体 このコルウト {

構造体 約束の種類 {

ThisCorout get_return_object ( { 戻る { } ; }

標準 :: 一時停止_決してしない 初期一時停止 ( { 戻る { } ; }

標準 :: 一時停止_決してしない ファイナルサスペンド ( 例外はありません { 戻る { } ; }

空所 未処理の例外 ( { }

空所 return_void ( { }

} ;

bool await_ready ( { 戻る 間違い ; }

空所 待機一時停止 ( 標準 :: コルーチン_ハンドル <> h { }

空所 再開を待つ ( { 標準 :: コート << 「コルーチンが再開されました。」 << 標準 :: 終わり ; }

} ;

このCorout foo ( {

標準 :: コート << 「コルーチンが開始されました。」 << 標準 :: 終わり ;

co_await 標準 :: 常にサスペンド { } ;

co_return ;

}

整数 主要 ( {

自動 cr = ふー ( ;

標準 :: コート << 「コルーチンが作成されました。」 << 標準 :: 終わり ;

cr. 再開を待つ ( ;

標準 :: コート << 「コルーチンが完了しました。」 << 標準 :: 終わり ;

戻る 0 ;

}

前に提供したコードを確認して、詳しく説明してみましょう。

必要なヘッダー ファイルをインクルードした後、コルーチンを表す「ThisCorout」構造体を定義します。 「ThisCorout」内には、コルーチン Promise を処理する「promise_type」という別の構造体が定義されています。この構造体は、コルーチン機構に必要なさまざまな機能を提供します。

括弧内では get_return_object() 関数を利用します。コルーチン オブジェクト自体を返します。この例では、空の「ThisCorout」オブジェクトが返されます。次に、initial_suspend() 関数が呼び出され、コルーチンが最初に開始されたときの動作を決定します。 std::suspend_never は、コルーチンが最初は一時停止されないことを意味します。

その後、コルーチンが終了しようとしているときの動作を決定するfinal_suspend()関数があります。 std::suspend_never は、コルーチンを終了前に一時停止しないことを意味します。

コルーチンが例外をスローすると、unhandled_Exception() メソッドが呼び出されます。この例では空の関数ですが、必要に応じて例外を処理できます。コルーチンが値を返さずに終了すると、return_void() メソッドが呼び出されます。この場合、これも空の関数です。

「ThisCorout」内に 3 つのメンバー関数も定義します。 await_ready() 関数は、コルーチンが実行を再開する準備ができているかどうかを確認するために呼び出されます。この例では、コルーチンをすぐに再開する準備ができていないことを示す false を常に返します。コルーチンが一時停止されるとき、メソッド await_suspend() が呼び出されます。ここでは、これは空の関数であり、一時停止の必要がないことを意味します。コルーチンが一時停止後に再開されると、プログラムは await_resume() を呼び出します。コルーチンが再開されたことを示すメッセージを出力するだけです。

コードの次の行では、foo() コルーチン関数を定義します。 foo() 内では、コルーチンが開始されたことを示すメッセージを出力することから始めます。次に、 co_await std::suspend_always{} を使用してコルーチンを一時停止し、後で再開できることを示します。 co_return ステートメントは、値を返さずにコルーチンを終了するために使用されます。

main() 関数では、foo() を呼び出して、タイプ「ThisCorout」のオブジェクト「cr」を構築します。これにより、コルーチンが作成され、開始されます。次に、コルーチンが作成されたことを示すメッセージが出力されます。次に、「cr」コルーチン オブジェクトに対して await_resume() を呼び出して、実行を再開します。 await_resume() 内で、「コルーチンが再開されました」というメッセージが出力されます。最後に、プログラムが終了する前にコルーチンが完了したことを示すメッセージが表示されます。

このプログラムを実行すると、出力は次のようになります。

例 2: パラメーターと Yielding を使用したコルーチン

この図では、C++ でパラメーターを指定したコルーチンの使用と生成を示し、一連の数値を生成するジェネレーターのような動作を作成するコードを提供します。

#include

#include <コルーチン>

#include <ベクター>

構造体 NEWコルーチン {

構造体 p_type {

標準 :: ベクター < 整数 > 価値観 ;

NEWCoroutine get_return_object ( ) { 戻る { } ; }

標準 :: 常にサスペンド 初期一時停止 ( ) { 戻る { } ; }

標準 :: 常にサスペンド ファイナルサスペンド ( ) 例外はありません { 戻る { } ; }

空所 未処理の例外 ( ) { }

空所 return_void ( ) { }

標準 :: 常にサスペンド 収量_値 ( 整数 価値 ) {

価値観。 プッシュバック ( 価値 ) ;

戻る { } ;

}

} ;

標準 :: ベクター < 整数 > 価値観 ;

構造体 イテレータ {

標準 :: コルーチン_ハンドル <> コーラスハンドル ;

ブール演算子 != ( 定数 イテレータ & 他の ) 定数 { 戻る コーラスハンドル != 他の。 コーラスハンドル ; }

イテレータ & オペレーター ++ ( ) { コーラスハンドル。 再開する ( ) ; 戻る * これ ; }

整数 オペレーター * ( ) 定数 { 戻る コーラスハンドル。 約束 ( ) 価値観 [ 0 ; }

} ;

イテレータの開始 ( ) { 戻る イテレータ { 標準 :: コルーチン_ハンドル < p_type >:: from_promise ( 約束 ( ) ) } ; }

イテレータの終了 ( ) { 戻る イテレータ { nullptr } ; }

標準 :: コルーチン_ハンドル < p_type > 約束 ( ) { 戻る
標準 :: コルーチン_ハンドル < p_type >:: from_promise ( * これ ) ; }

} ;

NEWコルーチンgenerateNumbers ( ) {

共同収量 5 ;

共同収量 6 ;

共同収量 7 ;

}

整数 主要 ( ) {

NEWコルーチン NC = 生成番号 ( ) ;

のために ( 整数 価値 : ノースカロライナ州 ) {

標準 :: コート << 価値 << 「」 ;

}

標準 :: コート << 標準 :: 終わり ;

戻る 0 ;

}

前のコードでは、NEWCoroutine 構造体はコルーチン ベースのジェネレーターを表します。これには、コルーチンの Promise タイプとして機能するネストされた「p_type」構造体が含まれています。 p_type 構造体は、get_return_object()、initial_suspend()、final_suspend()、unhandled_Exception()、return_void() などのコルーチン機構に必要な関数を定義します。 p_type 構造体には、コルーチンから値を取得するために使用される yield_value(int value) 関数も含まれています。提供された値を値ベクトルに追加します。

NEWCoroutine 構造体には、生成された値を表す「values」と呼ばれる std::vector メンバー変数が含まれています。 NEWCoroutine 内には、生成された値を反復処理できるネストされた構造体イテレーターがあります。これは、コルーチンへのハンドルである coro_handle を保持し、反復用の !=、++、および * などの演算子を定義します。

begin() 関数を使用して、p_type Promise から coro_handle を取得することで、コルーチンの先頭にイテレータを作成します。一方、 end() 関数は、コルーチンの終了を表すイテレータを作成し、nullptr coro_handle で構築されます。その後、promise() 関数を使用して、p_type プロミスから coroutine_handle を作成することでプロミス タイプを返します。 generateNumbers() 関数は、co_yield キーワードを使用して 3 つの値 (5、6、7) を生成するコルーチンです。

main() 関数では、generateNumbers() コルーチンを呼び出すことによって、「nc」という名前の NEWCoroutine のインスタンスが作成されます。これにより、コルーチンが初期化され、その状態が取得されます。範囲ベースの「for」ループを使用して「nc」の値を反復し、std::cout を使用してスペースで区切られた各値が出力されます。

生成された出力は次のとおりです。

結論

この記事では、C++ でのコルーチンの使用法を示します。 2 つの例について説明しました。最初の図では、基本的なコルーチンは、コルーチン関数を使用して C++ プログラムで作成されます。一方、2 番目のデモンストレーションは、パラメーターを使用してコルーチンを利用し、ジェネレーターのような動作を生成して一連の数値を作成することによって実行されました。