Linux プログラミング: 1. コルーチンの設計原則

1. プロセス

プロセスとは、実行中のプログラム インスタンスを指します。オペレーティング システムでは、各プロセスは独自のメモリ領域、実行ステータス、リソース使用量などの属性を持ちます。プロセスには、プロセスのメモリ空間やその他のリソースを共有する複数のスレッドを含めることができます。オペレーティング システムは、プロセス管理を通じてさまざまなプロセス間のリソースの割り当てを制御し、プロセスが相互に干渉することなく合理的に連携できるようにします。

第二に、スレッド

スレッドは、オペレーティング システムが操作のスケジューリングを実行できる最小単位です。スレッドID、プログラムカウンタ、レジスタセット、スタックで構成されます。スレッドはプロセス内のデータを共有しますが、各スレッドには独自のレジスタとスタック領域があるため、独立して実行でき、プリエンプトされた後に実行状態を再開できます。プロセスには少なくとも 1 つのスレッドがあり、プロセスにスレッドが 1 つだけある場合はシングルスレッド アプリケーションと呼ばれ、複数のスレッドがあるプロセスはマルチスレッド アプリケーションと呼ばれます。

3. コルーチン

1. コルーチンとは

コルーチンはユーザー モードの軽量スレッドであり、マイクロスレッドとも呼ばれます。コルーチンは、オペレーティング システムによって提供されるスレッドとは異なります。コルーチンは、プログラム内でユーザーによって制御およびスケジュールされます。実行を一時停止して現在の状態を保存し、実行を再開できる特別な関数またはコード ブロックとみなすことができます。必要なときに。コルーチンは通常、非同期プログラミング、高同時実行性、ネットワーク プログラミングなどの分野で使用され、プログラムのパフォーマンスとリソース使用率を効果的に向上させることができます。

2. コルーチンが存在する理由

コルーチンはプログラムを複数の独立したタスクに分割し、各タスクを一時停止および再開したり、実行中に他のタスクと交互に実行したりできます。これにより、スレッド切り替えやコンテキスト切り替えによるパフォーマンスの低下を回避し、プログラムの同時処理能力や効率を向上させることができます。

コルーチンは非同期プログラミングの実装にも利用でき、IOなどの時間のかかる操作をコルーチンの一時停止操作に変換することで、スレッド数やメモリ使用量を削減し、プログラムの保守性や拡張性を向上させることができます。

さらに、コルーチンを使用すると、非同期コードの複雑な記述も簡素化できるため、プログラマはビジネス ロジックの実装により集中して開発効率を向上させることができます。

3. プロセス、スレッド、コルーチンの違いは何ですか

  1. プロセスは、オペレーティング システムによるリソース割り当ての基本単位です。各プロセスには独自のメモリ領域、ファイル ハンドル、およびシステム リソースがあります。プロセスはデータやリソースを直接共有できません。プロセスの起動には、ある程度の時間とスペースのオーバーヘッドが必要です。
  2. スレッドはプログラム実行の最小単位であり、プロセスには複数のスレッドを含めることができます。スレッドはプロセスのアドレス空間とシステム リソースを共有し、スレッド間の通信はプロセス間通信よりも効率的です。
  3. コルーチンはマイクロスレッドまたはユーザーレベルのスレッドとも呼ばれ、プログラマによって制御される軽量のスレッドです。スレッドとは異なり、コルーチンにはオペレーティング システムのスケジューリングのオーバーヘッドがなく、アプリケーション内でのみ切り替えを完了する必要があります。コルーチンは通常、yield 関数または await キーワードを使用して、CPU 実行権を積極的に譲渡し、後で復元できるように現在の状態を保存します。

4. コルーチンの基本的な操作

  1. コルーチンの作成: 新しいコルーチンを作成し、指定された関数に割り当てます。
  2. コルーチンの開始: 作成したコルーチンを開始し、その中のコードの実行を開始します。
  3. コルーチンを一時停止する: 現在実行中のコルーチンを一時停止し、その状態を保存して、後で実行を再開します。
  4. コルーチンを再開する: 最後に中断された位置からコルーチンの実行を再開し、対応するパラメーターを渡します。
  5. コルーチンの終了: 現在実行中のコルーチンを終了し、対応する値を返します。
  6. 同期通信: メッセージの送受信や変数の共有など、2 つのコルーチン間の同期通信を実現します。
  7. 非同期通信: メッセージキューを介した通信など、2 つのコルーチン間の非同期通信を実現します。
  8. Mutex: 共有リソースを保護して、複数のコルーチンがそのリソースに同時にアクセスできないようにします。
  9. 条件変数: コルーチン間でイベントを待機および通知するためのメカニズム。
  10. タイマー: コルーチンでイベントをトリガーするタイマーを設定します。

5. コルーチンの切り替え

コルーチンの切り替えとは、コルーチンの実行中に現在のコルーチンの実行状態を一時停止し、別のコルーチンに切り替えて実行を継続することを指します。このスイッチは通常、Python の yield、asyncio ライブラリの await などの特別な構文を通じて実装されます。

コルーチンが yield または await を呼び出すと、自動的に制御が解放され、他のコルーチンが実行できるように CPU リソースが放棄されます。同時に、現在のコルーチンの実行状態が保存され、次回実行を再開できるようになります。他のコルーチンの実行が完了すると、制御は元のコルーチンに戻り、前のタスクの実行を継続します。

コルーチンの切り替えは、スレッド切り替えのようなシステム コールやコンテキスト切り替えを必要としないため、非常に高速です。また、コルーチンは軽量であるため、同じスレッド内に多数のコルーチンを作成して CPU リソースを最大限に活用し、プログラムの同時処理能力を向上させることができます。

6. コルーチンの構造体の定義方法

typedef struct coroutine_s {
    
    
    void (*func)(struct coroutine_s *); // 协程函数指针
    void *arg;         // 协程执行函数参数指针
    char *stack;       // 协程栈空间指针
    size_t stack_size; // 协程栈空间大小
    jmp_buf env;   // 协程上下文
    int status; // 协程状态 0: ready, 1: running, -1: finished
} coroutine_t;

7. コルーチンのスケジュールを定義する方法

typedef struct coroutine_s {
    
    
    void (*func)(struct coroutine_s *); // 协程函数指针
    void *arg;         // 协程执行函数参数指针
    char *stack;       // 协程栈空间指针
    size_t stack_size; // 协程栈空间大小
    jmp_buf env;   // 协程上下文
    int status; // 协程状态 0: ready, 1: running, -1: finished
} coroutine_t;

struct schedule {
    
    
    char stack[1024*1024];         // 协程池(每个协程分配的栈大小为1MB)
    ucontext_t main;               // 主函数上下文
    int running_coroutine;         // 当前运行的协程ID
    coroutine_t **coroutines; // 协程数组指针
    int max_index;                 // 最大协程数
    int cur_index;                 // 当前协程数
};

8. コルーチンのスケジューリング戦略を実装する方法

  1. 非プリエンプティブ スケジューリング: コルーチンが実行を終了するか、積極的に CPU を放棄すると、次に実行可能なコルーチンが実行のために選択されます。この方法は、比較的単純で軽量なタスクを含むシナリオに適しています。
  2. プリエンプティブ スケジューリング: コルーチンの実行時間が特定のしきい値を超えるか、IO などのブロック操作が発生すると、コルーチンは強制的に一時停止され、他のコルーチンが実行されます。この方法は、タスクが複雑で長時間実行する必要があるシナリオに適しています。
  3. 協調スケジューリング: コルーチンは切り替えるために明示的に調整する必要があります。つまり、コルーチンが自発的に CPU を放棄した場合にのみ、次のコルーチンがスケジュールされます。この方法は、ゲームエンジンのフレームレート制御など、よりきめ細かい制御が必要なシーンに適しています。
  4. マルチレベルのフィードバック キュー スケジューリング: すべてのコルーチンを、それぞれ異なる優先度を持つ複数の優先キューに分割します。新しいコルーチンは常に最も優先度の高いキューに入れられ、コルーチンが一定の時間を超えて実行されると、優先度の低いキューに移動されます。この方法は、タスクの実行効率と公平性のバランスが必要なシナリオに適しています。
  5. タイム スライス ラウンドロビン スケジューリング: CPU 時間をいくつかのタイム スライスに分割し、各コルーチンが一定期間実行された後、次のコルーチンに強制的に切り替えます。この方法は、複数のコルーチン間で CPU 時間を均等に分散する必要があるシナリオに適しています。

ゼロサウンドアカデミーの無料公開講座がおすすめ 個人的に先生の教え方が良かったと思うのでシェアしたいと思います。

Linux、Nginx、ZeroMQ、MySQL、Redis、fastdfs、MongoDB、ZK、ストリーミング メディア、CDN、P2P、K8S、Docker、TCP/IP、コルーチン、DPDK、その他の技術コンテンツを今すぐ学習

おすすめ

転載: blog.csdn.net/weixin_44839362/article/details/130537856