「コルーチン」と「スレッド」の違いは?


回答:


122

コルーチンは逐次処理の形式です。常に1つだけが実行されます(サブルーチンAKAプロシージャAKA関数と同じように、バトンを互いにより流動的に渡します)。

スレッドは(少なくとも概念的には)並行処理の形式です。複数のスレッドが同時に実行されている場合があります。(伝統的に、シングルCPU、シングルコアマシンでは、その同時実行性はOSの助けを借りてシミュレートされました-今日では、多くのマシンがマルチCPUやマルチコアであるため、スレッドは事実上同時に実行されますが、 「概念的に」だけではありません)。


188

最初に読む: 同時実行と並列処理-違いは何ですか?

並行性は、インターリーブ実行を提供するためのタスクの分離です。並列処理とは、速度を上げるために複数の作業を同時に実行することです。— https://github.com/servo/servo/wiki/Design

短い答え:スレッドを使用すると、オペレーティングシステムは、オペレーティングシステムカーネルのアルゴリズムであるスケジューラに従って、実行中のスレッドをプリエンプティブに切り替えます。コルーチンでは、プログラマーとプログラミング言語がコルーチンを切り替えるタイミングを決定します。言い換えると、タスクは、通常、(必ずしもではないが)単一のスレッド内で、設定されたポイントで関数を一時停止および再開することにより、協調的にマルチタスク処理されます。

長い答え:オペレーティングシステムによってプリエンプティブにスケジュールされるスレッドとは対照的に、コルーチンスイッチは協調的です。つまり、プログラマー(およびおそらくプログラミング言語とそのランタイム)は、スイッチが発生するタイミングを制御します。

プリエンプティブなスレッドとは対照的に、コルーチンスイッチは協調的です(スイッチがいつ発生するかをプログラマーが制御します)。カーネルはコルーチンスイッチに関与していません。— http://www.boost.org/doc/libs/1_55_0/libs/coroutine/doc/html/coroutine/overview.html

ネイティブスレッドをサポートする言語は、オペレーティングシステムのスレッド(カーネルスレッド)に対してそのスレッド(ユーザースレッド)を実行できます。すべてのプロセスには、少なくとも1つのカーネルスレッドがあります。カーネルスレッドは、所有プロセスのメモリ空間をそのプロセスの他のすべてのスレッドと共有することを除いて、プロセスに似ています。プロセスは、メモリ、ファイルハンドル、ソケット、デバイスハンドルなどの割り当てられたすべてのリソースを「所有」し、これらのリソースはすべてカーネルスレッド間で共有されます。

オペレーティングシステムスケジューラは、各スレッドを特定の時間(シングルプロセッサマシン上で)実行するカーネルの一部です。スケジューラーは各スレッドに時間(タイムライシング)を割り当て、その時間内にスレッドが完了しない場合、スケジューラーはスレッドをプリエンプトします(割り込みして別のスレッドに切り替えます)。各スレッドを個別のプロセッサにスケジュールできる(必ずしもそうである必要はない)ため、マルチプロセッサマシン上で複数のスレッドを並行して実行できます。

シングルプロセッサマシンでは、スレッドはタイムスライスされ、すばやく切り替えられます(Linuxでは、デフォルトのタイムスライスは100ミリ秒です)。ただし、シングルコアプロセッサは一度に1つしか実行できないため、これらを同時に(同時に)実行することはできません。

コルーチンジェネレーターを使用して、協調機能を実装できます。カーネルスレッドで実行され、オペレーティングシステムによってスケジュールされるのではなく、それらは、生成または終了するまで単一のスレッドで実行され、プログラマーによって決定された他の関数に変換されます。PythonやECMAScript 6などのジェネレーター備えた言語を使用して、コルーチンを構築できます。非同期/待機(C#、Python、ECMAscript 7、Rustで見られる)は、先物/約束を生成するジェネレーター関数の上に構築された抽象概念です。

コンテキストによっては、コルーチンがスタックフル関数を参照し、ジェネレーターがスタックレス関数を参照する場合があります。

ファイバーライトウェイトスレッドグリーンスレッドは、コルーチンまたはコルーチンのようなものの別名です。プログラミング言語のオペレーティングシステムスレッドのように(通常は意図的に)見えることがありますが、実際のスレッドのように並列実行されず、代わりにコルーチンのように動作します。(言語や実装によっては、これらの概念の中に、より具体的な技術的な特殊性や違いがある場合があります。)

たとえば、Javaには「グリーンスレッド」がありました。これらは、基盤となるオペレーティングシステムのカーネルスレッドではなく、Java仮想マシン(JVM)によってスケジュールされたスレッドです。これらは並列に実行されず、ネイティブスレッドが必要になるため、複数のプロセッサ/コアを利用しませんでした!それらはOSによってスケジュールされなかったので、カーネルスレッドよりもコルーチンのようでした。グリーンスレッドは、ネイティブスレッドがJava 1.2に導入されるまでJavaが使用していたものです。

スレッドはリソースを消費します。JVMでは、各スレッドに独自のスタックがあり、通常は1MBのサイズです。64kは、JVMでスレッドごとに許可される最小のスタックスペースです。スレッドスタックサイズは、JVMのコマンドラインで設定できます。名前にもかかわらず、各スレッドが独自のスタック、スレッドローカルストレージ(存在する場合)、およびスレッドスケジューリング/コンテキストスイッチング/ CPUキャッシュ無効化のコストを必要とするなどの使用リソースのため、スレッドはフリーではありません。これが、パフォーマンスが重要で並行性の高いアプリケーションでコルーチンが人気になった理由の1つです。

Mac OSはプロセスに約2000のスレッドのみを割り当て、Linuxはスレッドごとに8MBのスタックを割り当て、物理RAMに収まるだけの数のスレッドのみを許可します。

したがって、スレッドは(メモリ使用量とコンテキスト切り替え時間の観点から)最も重いウェイトであり、次にコルーチン、そして最後にジェネレーターが最も軽いウェイトです。


2
+1、しかしこの答えはいくつかの参照から利益を得る可能性があります。
小次郎2014年

1
緑のスレッドは、コルーチンとは異なるものがあります。彼らは違いますか?繊維にも違いがあります。programmers.stackexchange.com/questions/254140/…を

113

約7年遅れますが、ここでの答えは、コルーチン対スレッドに関するいくつかのコンテキストが欠落しています。なぜ最近コルーチンはそれほど注目されており、スレッドと比較していつ使用するのですか?

まず第一に、コルーチンが並行して実行されない場合(決して並行して実行されない)、なぜ誰かがスレッドよりもそれらを好むのでしょうか?

答えは、コルーチンはオーバーヘッドがほとんどなく非常に高いレベルの同時実行性を提供できるということです。一般に、スレッド環境では、スレッドが実際に(システムスケジューラによって)スケジュールされることでオーバーヘッドが無駄になり、実際にスレッドが実際に有用な作業を行う時間を大幅に削減するまでに、最大30〜50 のスレッドがあります。

スレッドでは並列処理を使用できますが、並列処理は多すぎません。単一スレッドで実行されるコルーチンよりも優れていますか?必ずしもそうではありません。コルーチンは、スケジューラーのオーバーヘッドなしに並行性を実行できることを覚えておいてください。それは単にコンテキスト切り替え自体を管理するだけです。

たとえば、ルーチンが何らかの作業を行っており、それがしばらくの間ブロックすることがわかっている操作(つまり、ネットワーク要求)を実行する場合、コルーチンを使用すると、システムスケジューラを含めるオーバーヘッドなしですぐに別のルーチンに切り替えることができます。この決定-はい、プログラマーコルーチンがいつ切り替えられるかを指定する必要があります。

多くのルーチンが非常に小さな作業を行い、自発的に相互に切り替えることで、スケジューラーが達成することを期待できない効率レベルに到達しました。数十のスレッドではなく、数千のコルーチンを連携させることができます。

ルーチンはあらかじめ決められたポイントを相互に切り替えるため、共有データ構造のロック回避することもできます(重要なセクションの途中で別のコルーチンに切り替えるようにコードに指示することはないため)。

もう1つの利点は、メモリ使用量がはるかに少ないことです。スレッドモデルでは、各スレッドが独自のスタックを割り当てる必要があるため、メモリの使用量はスレッドの数に比例して増加します。コルーチンでは、使用しているルーチンの数は、メモリ使用量と直接関係がありません。

そして最後に、一部のプログラミング言語(Pythonなど)ではスレッドがいずれにしても並列実行できないため、コルーチンが非常に注目されています。スレッドはコルーチンのように同時に実行できますが、メモリが少なく、スケジューリングのオーバーヘッドがありません


2
ブロック操作が発生したときにコルーチンで別のタスクに切り替えるにはどうすればよいですか?
Narcisse Doudieu Siewe 2016

別のタスクに切り替える方法は、ブロッキング操作を実際に非同期で実行することです。これは、実際にブロックされる操作の使用を避け、コルーチンシステムで使用する場合はブロックしないことをサポートする操作のみを使用する必要があることを意味します。これを回避する唯一の方法は、たとえばWindowsのUMSなどのカーネルがコルーチンをサポートするようにすることです。コルーチンは、syscallでUMSの「スレッド」がブロックするたびにスケジューラにジャンプします。
retep998

@MartinKonecny最近のC ++スレッドTSは、あなたが言及したアプローチに準拠していますか?
ニコス

したがって、最終的に、現代のプログラミング言語は、単一のCPUコアを効率的に使用して、たとえばIOやスレッドなどの非計算負荷の高い操作を多数のコアで並列化して速度を上げるために、両方のコルーチン/ファイバーを必要とするでしょう?
Mahatma_Fatal_Error 2018年

19

つまり、プリエンプションです。コルーチンは、十分にリハーサルされたポイントを互いに引き継ぎ続けるジャグラーのように機能します。スレッド(真のスレッド)は、ほぼいつでも中断され、後で再開できます。もちろん、これにはあらゆる種類のリソース競合の問題が伴います。そのため、Pythonの悪名高いGIL-グローバルインタープリターロックです。

多くのスレッド実装は、実際にはコルーチンに似ています。


9

使用している言語によって異なります。たとえばLua でも同じです(コルーチンの変数型はと呼ばれますthread)。

通常、コルーチンは、プログラマーが(どこで)どこを決定するか(yieldつまり、別のルーチンに制御権を与える)を自発的に譲り渡します。

代わりに、スレッドはOSによって自動的に管理(停止および開始)され、マルチコアCPUで同時に実行することもできます。


0

議論に12年遅れましたが、コルーチンは名前に説明があります。コルーチンはCoとルーチンに分解できます。

このコンテキストのルーチンは、一連の操作/アクションであり、ルーチンを実行/処理することにより、一連の操作は、指定された順序とまったく同じ順序で1つずつ実行されます。

Coは協力の略です。coルーチンは、他のco-ルーチンにも実行する機会を与えるために、その実行を喜んで一時停止するように求められます(または、より期待される可能性があります)。したがって、コルーチンはCPUリソースを(喜んで)共有して、他のユーザーが自分が使用しているのと同じリソースを使用できるようにすることです。

一方、スレッドはその実行を中断する必要はありません。中断されていることはスレッドに対して完全に透過的であり、スレッドは基盤となるハードウェアによって強制的に中断されます。また、通知されず、状態が変更されずに保存され、後でスレッドの続行が許可されたときに復元されるため、スレッドに対してほとんど透過的になるように行われます。

真実ではないことの1つは、コルーチンを同時に実行できず、競合状態が発生しないことです。コルーチンが実行されているシステムに依存し、コルーチンのイメージングが簡単に可能です。

コルーチンがどのように自分自身を一時停止するかは重要ではありません。Windows 3.1に戻ると、int 03はすべてのプログラムに組み込まれ(またはそこに配置する必要がありました)、C#では歩留まりを追加します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.