web-dev-qa-db-ja.com

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

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

129
jldupont

コルーチンはシーケンシャル処理の形式です:常に1つだけが実行されています(サブルーチンAKAプロシージャAKA関数と同様に、バトンを互いによりスムーズに渡します)。

スレッドは(少なくとも概念的には)並行処理の一種です。複数のスレッドがいつでも実行される可能性があります。 (従来、シングルCPU、シングルコアマシンでは、並行性はOSの助けを借りてシミュレートされていました-今日では、非常に多くのマシンがマルチCPUやマルチコアであるため、スレッドは事実上の事実です「概念的に」だけでなく、同時に実行する。

96
Alex Martelli

最初の読み取り:同時実行と並列処理-違いは何ですか?

並行性とは、インターリーブされた実行を提供するためのタスクの分離です。並列処理とは、速度を上げるために複数の作業を同時に実行することです。 — 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つのことしか実行できないため、それらを並行して(同時に)実行することはできません。

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

一部のコンテキストでは、coroutinesはスタックフル関数を参照し、generatorsは参照する場合がありますスタックレス機能に。

ファイバー軽量スレッド、および緑のスレッドは、コルーチンまたはコルーチンのようなものの別の名前です。プログラミング言語のオペレーティングシステムスレッドのように見えることがありますが、実際のスレッドのように並行して実行されることはなく、コルーチンのように動作します。 (言語または実装に応じて、これらの概念の間に、より具体的な技術的特殊性または違いがある場合があります。)

たとえば、Java has "green threads";これらはJava仮想マシン(JVM)。これらは、並列に実行されたり、複数のプロセッサ/コアを利用したりしませんでした-ネイティブスレッドが必要になるためです! OSによっては、カーネルスレッドよりもコルーチンのようでした。グリーンスレッドは、JavaネイティブスレッドがJava 1.2。

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

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

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

143
llambda

約7年遅れていますが、ここでの答えには、コルーチンとスレッドの関係が欠けています。 coroutinesが最近注目を集めているのはなぜですか?threadsと比較して、いつ使用するのですか?

まず、コルーチンがconcurrentlyparallelではない)で実行される場合、なぜスレッドよりもそれらを好むのでしょうか?

答えは、コルーチンは、非常に高いレベルの並行性を提供でき、非常に小さなオーバーヘッド。一般に、スレッド環境では、オーバーヘッドの量が実際にこれらのスレッドを(システムスケジューラーによって)浪費する前に、最大30-50のスレッドがありますsignificantlyスレッドが実際に有用な作業を行う時間。

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

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

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

ルーチンは互いに所定のポイントを切り替えるようになったため、共有データ構造でロックを回避できるようになりました(クリティカルセクションの途中でコードに別のコルーチンに切り替えるよう指示することはないためです) )

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

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

86
Martin Konecny

一言で言えば:プリエンプション。コルーチンはジャガーのように振る舞い、十分にリハーサルされたポイントを互いに渡し続けます。スレッド(真のスレッド)は、ほぼすべての時点で中断でき、後で再開できます。もちろん、これはあらゆる種類のリソース競合の問題をもたらすため、Pythonの悪名高いGIL-Global Interpreter Lockです。

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

18
Peter Rowell

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

通常、コルーチンは、プログラマーがyieldの場所を決める(つまり、別のルーチンに制御を与える)自発的な譲歩を実装します。

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

9
Thomas Bonini