import std.stdio; import core.thread; class SharedData { private int v; // privateに変更 public synchronized void increase() { int a = v; for(int i; i<1000000; i++){} // wait v = a + 1; } public int value() const // constをなくすとエラー(※) { return v; } } class IncrementThread : Thread { private shared(SharedData) sd; public this(shared(SharedData) sd) { this.sd = sd; super(&f); } private void f() { sd.increase(); } } void main() { // 全部のスレッドで共有するデータ auto sd = new shared(SharedData); // 10個のスレッドを次々に作って実行開始 Thread[] threads; for(int i; i<10; ++i) { auto t = new IncrementThread(sd); threads ~= t; t.start(); } // すべてのスレッドが終了するのを待つ foreach(t; threads) { t.join(); } // インクリメント結果を表示 writeln(sd.value); // constメンバ関数はshared型オブジェクトを介しても正しく呼び出せる(※) }
なん・・・だと・・・!?
実は最初に公開したコードでは,一連のサンプルコードを作るに当たり,計算後のvの値を得るgetterを作っていた.
しかし,非synchronizedメンバ関数は,shared型のオブジェクトからは呼び出せなかった.
そこで,仕方なくvをpublic変数に変更した.
しかし,改めて考えてみると,shared型のオブジェクトから非synchronizedメンバ関数を呼び出せないというのは重大な問題だと思われる.
そこで,本当にそんな問題があるのかもう一度落ち着いて調べてみようとした.
すると,久保先生が脳内で冒頭の言葉を叫んだ.
なんと,getterをgetterらしくちゃんとconstメンバ関数にすると,非synchronizedメンバ関数であっても,shared型のオブジェクトから呼び出すことができることがわかった.
しかもこのようにすると,shared型の変数をwrite系の関数が受け取ったときに数値が表示されず型を文字列化したものが表示される問題も回避できる.
もしエラーメッセージが適切なら,これらの素晴らしい事実にもっと早く気がつけたはず.
ちなみにgetterを非constにしたときのエラーはこうだ.
main.d(51): Error: function main.SharedData.value () is not callable using argument types ()
こんなメッセージでわかるか!
是非とも改善して欲しいところである.
追加
constメンバ関数をshared型のオブジェクトから呼び出せるのは問題が出る場合があるらしい.