スレッドは、オペレーティングシステムとプログラミング言語の2つの観点から登場します。どちらの場合も、スレッドの属性には多少の違いがあります。
スレッドの最小限の定義は、順番に発生するものであるということです。
典型的なマシン実行モデルでは、各スレッドには独自の汎用レジスターセットと独自のプログラムカウンターがあります。マシンがスタックポインタとして特定のレジスタを設定する場合、スレッドごとに1つのコピーがあります。
オペレーティングシステムの観点から見ると、オペレーティングシステムがスレッドをサポートするために最低限必要なことは、スレッドを切り替える方法を提供することです。これは自動的に発生する可能性があります(予測マルチタスクまたはスレッドが明示的な要求を行う場合のみ)(協調マルチタスク。この場合、スレッドはファイバーと呼ばれることもあります)。たとえば、異なるグループのスレッド間のプリエンプションまたは、同じグループ/タスクのスレッド間で明示的な収量が得られます。スレッド間の切り替えには、少なくとも古いスレッドのレジスタ値を保存し、新しいスレッドのレジスタ値を復元する必要があります。
タスク(またはプロセス)を分離するマルチタスクオペレーティングシステムでは、これらの用語をOSコンテキストの同義語として扱うことができます。各タスクには独自のリソース、特にアドレススペースがありますが、オープンファイル、特権などもあります。上記のプロセスのエンティティであるオペレーティングシステムカーネルによって提供されます。通常、各タスクには少なくとも1つのスレッドがあります。コードを実行しないタスクはあまり役に立ちません。オペレーティングシステムは、同じタスクで複数のスレッドをサポートする場合としない場合があります。たとえば、元のUnixはそうではありませんでした。タスクは、スレッドを切り替えることで複数のスレッドを実行できます。これには特別な特権は必要ありません。これは「ユーザースレッド」と呼ばれます」、特にUnixコンテキストで。現在、ほとんどのUNIXシステムはカーネルスレッドを提供しています。これは、特に、異なるプロセスで同じプロセスの複数のスレッドを実行する唯一の方法だからです。
計算時間を除くほとんどのオペレーティングシステムリソースは、スレッドではなくタスクに関連付けられます。一部のオペレーティングシステム(Linuxなど)は、スタックを明示的に区切ります。この場合、各スレッドには独自のスレッドがあります。しかし、カーネルがスタックについて何も知らないOSがあります。それらは、スタックに関する限り、ヒープの一部にすぎません。カーネルは通常、各スレッドのカーネルコンテキストも管理します。これは、スレッドが現在何をしているかに関する情報を含むデータ構造です。これにより、カーネルはシステムコールでブロックされた複数のスレッドを同時に処理できます。
オペレーティングシステムに関する限り、タスクのスレッドは同じコードを実行しますが、そのコード内の異なる位置(異なるプログラムカウンター値)にあります。プログラムのコードの特定の部分が常に特定のスレッドで実行される場合とそうでない場合がありますが、通常は、任意のスレッドから呼び出すことができる一般的なコード(ユーティリティ関数など)があります。すべてのスレッドは同じデータを参照しますが、そうでない場合は異なるタスクと見なされます。特定のスレッドのみがアクセスできるデータがある場合、それは通常、オペレーティングシステムではなく、プログラミング言語の範囲内にあります。
ほとんどのプログラミング言語では、ストレージは同じプログラムのスレッド間で共有されます。これは、並行プログラミングの共有メモリモデルです。競合状態が発生する可能性があるため、複数のスレッドが同じデータにアクセスできる場合、プログラマは注意する必要があるため、非常に人気がありますが、非常にエラーが発生しやすくなります。ローカル変数でさえスレッド間で共有できることに注意してください。「ローカル変数」(通常)は、関数の1回の実行中にのみ名前が有効な変数を意味しますが、別のスレッドはその変数へのポインターを取得してアクセスできます。
また、各スレッドに独自のストレージがあり、通信チャネルを介してメッセージを送信することでスレッド間の通信が行われるプログラミング言語もあります。これは、並行プログラミングのメッセージパッシングモデルです。アーランメッセージの受け渡しに焦点を当てた主要なプログラミング言語です。その実行環境はスレッドの処理が非常に軽量であり、スレッドの作成が比較的高価な操作であり、ランタイム環境が非常に大規模をサポートできない他のほとんどのプログラミング言語とは対照的に、多くの短命スレッドで書かれたプログラムを奨励します同時にスレッドの数。Erlangのシーケンシャルサブセット(スレッド内で発生する言語の一部、特にデータ操作)は(ほとんど)純粋に機能的です。したがって、スレッドはデータを含む別のスレッドにメッセージを送信できます。どちらのスレッドも、使用中に他のスレッドによってデータが変更されることを心配する必要はありません。
一部の言語では、スレッドローカルストレージをグローバルシステムと区別するための型システムの有無にかかわらず、スレッドローカルストレージを提供することで2つのモデルをブレンドしています。スレッドローカルストレージは通常、変数名が異なるスレッドの異なるストレージの場所を指定できるようにする便利な機能です。
スレッドが何であるかを理解するのに興味深いかもしれないいくつかの(難しい)フォローアップ:
- 複数のスレッドをサポートするためにカーネルが行う必要のある最低限のものは何ですか?
- マルチプロセッサ環境で、あるプロセッサから別のプロセッサにスレッドを移行するには何が必要ですか?
- オペレーティングシステムのサポートなしで、組み込みのサポートを使用せずに、お気に入りのプログラミング言語で協調型マルチスレッド(コルーチン)を実装するには何が必要ですか?(ほとんどのプログラミング言語には、単一のスレッド内にコルーチンを実装するために必要なプリミティブがないことに注意してください。)
- 並行性はあるが(明示的な)スレッドの概念がない場合、プログラミング言語はどのように見えるでしょうか?(主な例:パイ計算)