前回はCPU時間割り当て対象スレッドを常時把握するメカニズムができた。今日はスレッドの増減に対応したい。
スレッドの生成と破棄
スレッドを生成したり破棄したりするタイミングは当メカニズムを利用するユーザープログラムの勝手なので、両者の順序に特段の規則は無い。よって、順序非依存でもっとも単純な管理方法として、
- 生成時、新規スレッドをリストの末尾に追加
- 破棄時、指定したスレッドをリストから削除
とすることにしよう。以下、このリストを(生成スレッドの略で)GTリストと呼ぶ。リスト要素を効率よく破棄するために、GTリストは双方向リストにする*1。また、そもそも要素の破棄が可能なためには、要素を外から指定できる必要があり、要素を識別するIDが要る。と考えていくと、詳細な手順はこんな感じになる。
- 生成手順:
- スレッドを生成(xとおく)
- xをGTリストに登録
- xのIDを返す
- 破棄手順:
- 対象スレッドxをIDで指定する
- IDから、GTリスト内のスレッドxを特定
- xをGTリストから削除
- xを開放
GTリスト検討(1)
GTリストの要素(GTリスト要素)と荷物(スレッド)の関係を詰める。双方向リストをC/C++でテンプレート禁止縛りで汎用コンテナとして設計しようとすると、リスト要素からvoid*ポインタで荷物にリンクするという形にならざるを得ない。
GTリスト要素a → スレッドw
↓↑
GTリスト要素b → スレッドx
↓↑
GTリスト要素c → スレッドy
このようにリスト要素と荷物が単方向リンクの場合、荷物が指定されても対応するリスト要素を特定しようがなく、リストからの削除が遂行できない。よって、スレッドID = GTリスト要素の(広義の)アドレス、である必要がある。
GTリスト検討(2)
何を格納するかというと、とりあえずTCtx構造体と考えれば十分。TCtx構造体を丸ごと憶えておけば、スレッドのexecute、suspend、resumeには事足りるはず。問題があれば削る。
GTリスト検討(3)
GTリスト要素+荷物は増えたり減ったりする。これらのメモリは動的に確保せねばならないが、それをグローバルなヒープ領域から(malloc()やnew等で)取得することは得策だろうかいいや得策ではない(反語
仮に本メカニズムの動的メモリ確保がいつも固定長で済んだとしても、ユーザープログラムとスタティックリンクする前提だとユーザープログラムもグローバルなヒープ領域からメモリを使用してくれるので断片化が避けられない。
結論
- GTリスト要素の配列をあらかじめ作っておき、その中で独自にメモリ管理する。
- 検討(1)の構造は却下。GTリストの荷物は、リスト要素からリンクするのでなく、リスト要素にコンポジション集約する。(さもないと、荷物で使う分のメモリ管理が別途必要になる。)
- IDは、上記配列のインデックスとする(ポインタより場所を食わない)。
- 似たようなリスト構造は今後も使いそうなので、テンプレートを使ってジェネリックに作る…としたいところだがそれだけのためにC++にするのも何なので、Cとプリプロセッサで何とか乗り切るようにする
方針2.には明白に問題が残ってる…
明日はスレッドの増減に対応したい(何