優先度の高いタスクから先にCPUを割り当てるスケジューリング方式。
優先度方式とは、各タスク(=CPUに処理してほしい仕事)に優先度という順位を付けておき、優先度の高いものから先にCPUを割り当てるスケジューリング方式のことです。
身近な例で考えると、病院の救急外来に似ています。受付に来た順ではなく、症状の重い人(=優先度の高い人)から先に診察する。緊急性の高い仕事を後回しにしないための考え方です。
上のツールで▶ボタンを押すと、レディーキュー(=順番待ちの列)から最も優先度の高いタスクが選ばれて実行され、優先度の高い順にガントチャート(=横軸が時間の実行記録)が埋まっていく様子を確認できます。低い優先度のタスクほど後回しになる点に注目してください。
優先度は何をもとに決めるのでしょうか。よく使われる基準は次のとおりです。
・重要度:システムにとって重要な処理(OSの中核処理など)を高くする
・締切(デッドライン):早く終わらせる必要がある処理を高くする
・処理時間:短く済むタスクを先に片付ける、といった考え方もある
優先度の付け方には大きく2種類あります。
・静的優先度:あらかじめ決めた優先度を最後まで変えない方式。単純で分かりやすい
・動的優先度:実行の状況に応じて優先度を変える方式。代表例がエイジング(=待ち時間が長いタスクの優先度を少しずつ上げる仕組み)
エイジングを使うと、最初は優先度が低くても「長く待たされたタスク」はだんだん優先度が上がり、やがて必ず実行されるようになります。次のカードで説明する飢餓状態を防ぐための工夫です。
優先度方式の弱点が飢餓状態(starvation)です。これは、低優先度のタスクが、次々にやってくる高優先度タスクに割り込まれ続け、いつまでも実行されない状態のことです。
身近な例で考えると、行列に横入りされ続ける状況に似ています。自分の前に「急いでいる人」が次から次へと割り込んでくると、自分の順番は永遠に回ってきません。優先度方式では、高優先度のタスクが絶え間なく到着すると、低優先度のタスクがまさにこの状態になります。
対策が前のカードで触れたエイジングです。待っている時間が長くなるほど優先度を少しずつ上げていけば、最初は低優先度でもいつかは高優先度に追いつき、必ず実行されるようになります。上のツールで、低優先度のDが他のタスクをすべて待ってから最後に実行される様子を確認してみてください。
| 方式 | 選ぶ基準 | 向いている場面 | 弱点 |
|---|---|---|---|
| FCFS | 到着時刻 | バッチ処理 | コンボイ効果(先頭が長いと全員が待つ) |
| ラウンドロビン | 順番(公平) | 対話型・Web | 重要な処理も後回しになることがある |
| 優先度方式 | 重要度・緊急度 | リアルタイム・OS中核 | 低優先度が飢餓に陥りやすい |
FCFS とラウンドロビンは「公平さ」に着目した方式で、どのタスクも等しく扱います。しかし現実には、すべての仕事が同じ重要度であることはほとんどありません。たとえば OS の中核処理(キーボード入力の受付など)は、一般のアプリより素早く終わらせる必要があります。
優先度方式が解決するのはまさにこの点です。重要な処理には高い優先度を付けて先に実行させることで、「締め切りまでに終わらせなければならない仕事」や「遅れると致命的な処理」を確実に素早く終わらせることができます。一方でトレードオフ(=一方を良くすると別の何かが悪くなる)として、低優先度のタスクが待たされやすいという弱点があります。上のツールで各シナリオを切り替え、優先度の順に実行されていくことを確認してみてください。
優先度方式にもノンプリエンプティブ(非横取り)とプリエンプティブ(横取りあり)の2種類があります。
・ノンプリエンプティブ優先度方式:実行が始まったタスクは途中で止めない。次の選択のときだけ優先度を見る。実装が簡単だが、高優先度の新しいタスクが来ても今のタスクが終わるまで待つ
・プリエンプティブ優先度方式:実行中でも、より優先度の高いタスクが到着したら即座に中断してそちらに切り替える。緊急性の高い処理に向くが、コンテキストスイッチ(=タスクを切り替えるための準備作業)のコストが増える
身近な例で考えると、ノンプリエンプティブは「今の電話が終わるまで緊急連絡を受けない」、プリエンプティブは「緊急電話が来たら今の電話を保留にして切り替える」ようなイメージです。現代の OS はほとんどプリエンプティブを採用しており、1つのアプリが応答しなくなっても他のアプリが動き続けられるのはこの仕組みのおかげです。