あなたの質問に直接答えようとする代わりに、マルチスレッド化された Haskell プログラムがどのように実装されるかの概念モデルを提供しようとします。多くの詳細と複雑さは無視します。
オペレーティング システムは、ハードウェア割り込みを使用してプリエンプティブ マルチスレッドを実装し、計算の複数の「スレッド」を同じコアで同時に論理的に実行できるようにします。
オペレーティング システムによって提供されるスレッドは、重量が重い傾向があります。これらは、特定のタイプの「マルチスレッド」アプリケーションによく適しており、Linux などのシステムでは、複数のプログラムを同時に実行できる基本的に同じツールです (優れたタスク)。
ただし、これらのスレッドは、Haskell などの高水準言語での多くの用途には少し重いです。基本的に、GHC ランタイムはミニ OS として機能し、OS スレッドの上に独自の「スレッド」を実装します。これは、OS がコアの上にスレッドを実装するのと同じ方法です。
Haskell のような言語がこのように実装されることは、概念的に容易に想像できます。Haskell の評価は、「サンクの強制」で構成されます。ここで、サンクは、1. 別の値 (サンク) に依存したり、2. 新しいサンクを作成したりする可能性のある計算単位です。
したがって、それぞれが同時にサンクを評価する複数のスレッドを想像することができます。評価されるサンクのキューを構築します。各スレッドは、キューの先頭をポップし、そのサンクが完了するまで評価してから、キューから新しいサンクを選択します。操作par
とその同類は、そのキューにサンクを追加することで、新しい計算を「開始」できます。
このモデルを IO アクションに拡張することも、想像に難くありません。それぞれが単純に純粋なサンクを強制する代わりに、Haskell 計算の単位がいくらか複雑になると想像します。このようなランタイムの疑似 Haskell:
type Spark = (ThreadId,Action)
data Action = Compute Thunk | Perform IOAction
注: これは概念を理解するためだけのものです。物事がこのように実装されているとは思わないでください。
Spark を実行すると、そのスレッド ID に「スロー」された例外を探します。何もないと仮定すると、実行は、サンクを強制するか、IO アクションを実行することで構成されます。
明らかに、ここでの私の説明は非常に手の込んだものであり、複雑さを無視しています。詳細については、GHC チームは、Marlow らによる「Runtime Support for Multicore Haskell」などの優れた記事を書いています。また、オペレーティング システムのテキスト ブックも参照することをお勧めします。スケジューラの作成方法について詳しく説明されていることがよくあります。