⑤優先度設計

優先度の設計です。

書き込みするスレッドの優先度を高くしうまく排他するなどです。

うかつに優先度を変更してしまって動作がおかしくなる可能性があります。

全体設計をしっかりする必要があるわけです。


⑥スレッド化

本来ひとつの資源を操作するスレッドを1つであるべきという視点。

再入の根本的な原因は複数のスレッドが資源を操作していることにあります。

管理スレッドを1つつくり、スレッドは管理スレッドに依頼する形で資源を利用する形です。

お願いの手段ですが、ここで登場するのがタスク間通信です。

★で示したような記述を追加してしてみます。

void log_output(char* message)
{
..static char buf[256];
..time_t t;

..time(&t);

★..kz_getsem(ID);

..strcpy(buf,ctime(&t));
..strcpy(buf+strlen(buf),message);

..puts(buf);

★..kz_relsem(ID);
}


kz_getsem()は仕様として実現したいこととしてすでに別スレッドによってセマフォが獲得されていた場合は、その資源がロックされているとみなしスレッドをスリープ状態にして待ち合わせます。

セマフォは獲得・解放をカウンタで管理することで、複数のスレッドが再入する場合のスレッド数の上限を管理することができます。

上限が1のセマフォはバイナリ・セマフォ(ミューテックス)と呼ばれ、常にひとつのスレッドしか実行できないことになり、完全な排他ができます。
★で示したような記述を追加してしてみます。

void log_output(char* message)
{
..static char buf[256];
..time_t t;
★..volatile static int locked=0;

..time(&t);

★..while(locked){
★....sleep(1);
★..}
★..locked=1;

..strcpy(buf,ctime(&t));
..strcpy(buf+strlen(buf),message);

..puts(buf);

★..locked=0;
}

ロックをかけるということを実施しています。
でもこれだと駄目。。

locked=0になりwhile(locked)を抜けたタイミングでスレッドが切り替わるとする。
locked=1は実行されていない状態。

切り替わったスレッドでもlog_output関数を実施した場合while(locked)を抜けた状態になったとする。
そうすると複数のタスクでstrcpy関数を実施しbuf[]を編集することになるわけだ。
そうなるとlockedが機能していないことになる。
lockedを参照してlocked=1をアトミックに行えないといけないということがわかる。
そのような動作を1命令で実施してくれるCPUがあるのだがこのような命令をTAS命令(TestAndSet)と呼ぶということだ。
似た処理でUNIX系OSではディレクトリ(ファイルではなく)の存在チェックと作成がアトミックに行えるようだ。