KHPC ver.0.02の内部資料的なもの
基本仕様
- KHPCの3番目のバージョン。KHPCはpersistent-Cの簡易実装を目指したツール群。
- 基本方針: とにかくできるだけ手抜きをする。次のバージョンでまともにすればいい。手抜きは美徳である。
- このバージョンの目玉:永続変数を共有したプログラム間通信に配慮
ダウンロード
サンプルコード(1)
#include <stdio.h>
#include "khpc0.h"
persistent int i = 0;
int main(int argc, const char **argv)
{
int m = 0;
if (argc >= 2)
sscanf(argv[1], "%d", &m);
for (;;) {
persistent_load(0);
if (i % 2 == m) {
printf("i=%d\n", i);
i++;
persistent_save(0);
}
}
return 0;
}
- こんなプログラムを作って、test1.cで保存してビルドして、コンソールを2つ開いて、
prompt>test1 0
prompt>test1 1
- で起動すると、1つ目のコンソールは偶数に反応して1を加算し、もう一つのコンソールは奇数に反応して1を加算するので、数字が増え続けます。
- ただし、このプログラムを実行するとOSによってはHDDやSSDの寿命を大幅に縮めるような過酷な書き込み命令を発行してしまうかもしれません。
- もしそれが心配ならRAMディスクなどを用意して、その上での実行をお勧めします。
- 川合は SoftPerfect DAM Disk というツール(Windows用)を使って実行しています。
サンプルコード(2)
- 簡単な早押しクイズゲームを作ってみました。
- クイズゲームとはいっても、答えるのはコンピュータです。
- test2m.c(マスター側):
#include <stdio.h>
#include <stdlib.h>
#include "khpc0.h"
persistent int m_sn, m_a, m_b;
struct SlaveData {
int sn, ans;
};
persistent struct SlaveData slv[5];
int main(int argc, const char **argv)
{
int a, b, sn = 1, i, n;
int score[5];
for (i = 0; i < 5; i++)
score[i] = 0;
persistent_load(0);
for (;;) {
/* 問題を作って表示 */
a = (rand() / 256) % 100;
b = (rand() / 256) % 100;
printf("Q.%d: %d+%d=? ", sn, a, b);
do {
/* 永続変数を使って問題をスレーブに提示する */
m_sn = sn;
m_a = a;
m_b = b;
persistent_save(0);
persistent_load(0);
/* 回答チェック */
n = 0;
for (i = 0; i < 5; i++) {
if (slv[i].sn == sn && slv[i].ans == a + b) {
printf("%d!(#%d) ", slv[i].ans, i);
score[i]++;
n++;
}
}
} while (n == 0);
putchar('\n');
/* スコア表示 */
for (i = 0; i < 5; i++)
printf("[#%d:%d] ", i, score[i]);
putchar('\n');
putchar('\n');
sn++;
}
return 0;
}
- test2s.c(スレーブ側):
#include <stdio.h>
#include "khpc0.h"
persistent int m_sn, m_a, m_b;
struct SlaveData {
int sn, ans;
};
persistent struct SlaveData slv[5];
int main(int argc, const char **argv)
{
int i = 0;
if (argc >= 2)
sscanf(argv[1], "%d", &i);
for (;;) {
persistent_load(0);
slv[i].sn = m_sn;
slv[i].ans = m_a + m_b;
persistent_save(0);
}
return 0;
}
- マスターは1つだけ起動します。スレーブは0~4の引数を付けて起動します。
- マスターが出題し、スレーブが回答します。マスターは得点の管理もします。
- 得点は永続変数ではないので、マスターをCtrl-Cして再起動すると全部0点に戻ります。
- 点数を永続変数にしないのは、永続変数にすると処理がちょっと面倒になるからです(後述)。
- こちらもRAMディスク上での実行をお勧めします。
- 建前としては「早押し」ということになっていますが、実際は必ずしも早押しにはなっていません。
- 出題されてマスターにsaveされて、その直後に運よくloadできて答えをsaveできたスレーブが「勝つ」べきなのですが、もしかしたらその直後にのろまなプログラムがいて周回遅れの回答をsaveするかもしれません(たまに起こりうる)。この場合、答えは上書きされてしまうので、先に答えたスレーブの結果はなくなってしまいます。
- 書き込む場所は slv[1] と slv[3] とで違っているのでそんな上書き問題はないと思うかもしれませんが、slv[1]の内容は前回load時の値がそのまま使われるだけなので、のろまさんが再読み込みしない限り最新の値は反映されないのです。
- この上書き問題がありうるので、マスターは毎回save前に出題を書き直しています。
- この上書き問題があるので、点数は永続変数を使っていません。スレーブによって加算される前の点数が保存され、それをマスターが読みだしてしまうと、もはや訳が分からなくなりそうです。
- マスターはとにかく回答が検出できるまでは次の問題には進まないので、のろまなプログラムに邪魔されても、めげずにまた書き込めば、勝つチャンスはあります。
議論
- やっかいな上書き問題をひとまず無視すれば、このプログラム例はかなりシンプルだと思う。OSに依存したスレッド制御命令を必要とせず、とても単純な機構で変数を共有化できている。
- 更新が中途半端なところで保存されることはないし、ロードが中途半端の段階で見えてしまうということもないので、データの整合性は常に保障される。
- 上書き問題をどうするかであるが、変数をグループに分けて、このプログラムではこの変数群は読み込みはするが保存はしない、この変数群は保存はするが読み込みはしない、などの設定で解決することができる(その場合グループごとにセーブファイルの名前も違うようにしておく)。その機能はすでにこのバージョンに組み込まれているが、使い方が少し面倒なので紹介していない。
- 書き換え頻度が高くなれば、上書き問題は大きな問題にはならないし、それに単に変数を永続化することの延長で変数の共有化を記述できるのなら、手軽さはかなりのものだと思う。
関数群
メモなど
こめんと欄