meキーワード
meキーワードは現在のshredへの自己参照になっている。ここでは三つの関数についてメモ。
- me.exit():現在のshredを終了する
- me.id():現在のshredのIDを返す
- me.yield():VMへ制御を渡す
me.exit()は読み込みたいファイルが開けなかったときやイベントの待ち行列から抜けるときに使える。
me.yield()は時間を進めず現在のshredを止めたままで、VMに他のshredのスケジューリングをさせたいときに使う。shredをsporkしたりイベントのsignal()やbroadcast()をした直後に時間を止めたままだと呼び出し側のshredからVMに制御が移らないんだけど、それでも呼び出された側のshredを実行させたい場合に強制的に制御を移すのがme.yield()*1。
例えば、並行性で載せたプログラム
fun int test(int x) { <<<x, " in test function">>>; return x; } <<<test(3)>>>; // 単なる関数呼び出し <<<spork ~ test(8)>>>; // sporkで関数からshredを作る 1::samp => now; // 親の終了を少し遅らせる
の最後の行は「作られたshredが実行される前に親が終了してしまうのを防ぐために必要」と書いたがこの行は「me.yield();」でも良い。というか意味的には本来こっちを使うような気がする。
色々混ぜてもう少し凝った例を作ってみた。
Event event; fun void bug(Event e, string s, int life) { <<<"---sporked a shred: id", me.id()>>>; while(e => now) { if (life <= 0) { // eの待ち行列から抜ける <<<s, "bug is dead.">>>; me.exit(); } <<<s, " life:", life>>>; life--; } } spork ~ bug(event, "1st", 3); spork ~ bug(event, "2nd", 1); spork ~ bug(event, "3rd", 3); me.yield(); // sporkされた三つのshredのため <<<"---make a signal twice", "">>>; event.signal(); event.signal(); me.yield(); // 送ったsignal()で呼ばれた二つのshredのため spork ~ bug(event, "4th", 3); me.yield(); // sporkされたshredのため <<<"---broadcast", "">>>; event.broadcast(); me.yield(); // 送ったbroadcast()で呼ばれたshredのため <<<"---broadcast", "">>>; event.broadcast(); me.yield(); // 送ったbroadcast()で呼ばれたshredのため
コンソール出力は以下。
---sporked a shred: id 2 ---sporked a shred: id 3 ---sporked a shred: id 4 ---make a signal twice 1st life: 3 2nd life: 1 ---sporked a shred: id 5 ---broadcast 3rd life: 3 1st life: 2 2nd bug is dead. 4th life: 3 ---broadcast 3rd life: 2 1st life: 1 4th life: 2
sporkやイベント発生の直後にme.yield()しているのでちゃんと制御が移っている。もし、上のプログラムから「me.yield();」という行を全部抜いて実行すると、出力は
---make a signal twice ---broadcast ---broadcast
だけになってしまう。
VMの動作の詳細を知らなくても、よほど変わったことをしない限りこの辺りを押さえておけば充分じゃないかな。
*1:多分。ChucKの並行性はノンプリエンプティブだそうで、それと関係あるんだと思うが自信ない…