Single Threaded Executionの続き
Single Threaded Executionが期待したように動作しない件。
"BROKEN"のメッセージが出力されるのは、Gate.Pass()内のgate.name = nameが実行され、次のgate.address = addressが実行される前に、他のgoroutineでGate.Check()が実行されたとき。でも、この2つの代入文の間でgoroutineがディスパッチされなければいけないが、2つの代入文の間でシステムコール呼び出しが行われているわけでもないので、期待通りにgorotineがディスパッチされなかったのだろう。Javaならなぜ期待通りになるのかという疑問は残るが。
なので、この2つの文の間でsyscall.Sleep(1)を実行してみると、"BROKEN"メッセージが出力されるようになった。
だが、この2つの代入文の間にシステムコールの呼び出しを入れるのは、サンプルのストーリーが不自然なので、Gate.Pass()をGate.Register()とGate.Pass()に分け、UserThreadは、自オブジェクトが持っている名前とアドレスをGateオブジェクトに登録してから、Gateを通る。Gateを通る時に、登録した名前とUserThreadが持っている名前の先頭1文字が一致するかの検査を行う、というストーリーに変更した。そして、名前と住所をGateオブジェクトに登録したあと、それをDBに反映するということにして、スレッドの実行を1nsだけスリープするようにした。
package main; import "fmt"; import "syscall"; // Gate class type Gate struct { counter int; name string; address string; } func (gate *Gate) Init() { gate.counter = 0; gate.name = "Nobody"; gate.address = "Nowhere"; } func (gate *Gate) Check(name string) { if gate.name[0] != name[0] { fmt.Printf("***** BROKEN ***** No.%d: %s, %s\n", gate.counter, name, gate.name); } } func (gate *Gate) Register(name string, address string) { gate.name = name; gate.address = address; syscall.Sleep(1); } func (gate *Gate) Pass(userThread *UserThread) { gate.counter++; gate.Check(userThread.myname); } // UserThread class type UserThread struct { gate *Gate; myname string; myaddress string; } func (userThread *UserThread) Init(gate *Gate, myname string, myaddress string) { userThread.gate = gate; userThread.myname = myname; userThread.myaddress = myaddress; } func (userThread *UserThread) Run() { fmt.Printf("%s BEGIN\n", userThread.myname); for { userThread.gate.Register(userThread.myname, userThread.myaddress); userThread.gate.Pass(userThread); } } // main func main() { gate := new(Gate); alice := new(UserThread); alice.Init(gate, "Alice", "Alaska"); bobby := new(UserThread); bobby.Init(gate, "Bobby", "Brazil"); chris := new(UserThread); chris.Init(gate, "Chris", "Canada"); go alice.Run(); go bobby.Run(); chris.Run(); }
こうすることで、名前を登録後にスレッドが切り替わるようになり、期待通り、goroutine間でデータが競合するようになった。
なので、データが競合しないようにするためにクリティカルセクションをMutexでロックするようにする。
var mutex sync.Mutex; func (userThread *UserThread) Run() { fmt.Printf("%s BEGIN\n", userThread.myname); for { mutex.Lock(); userThread.gate.Register(userThread.myname, userThread.myaddress); userThread.gate.Pass(userThread); mutex.Unlock(); } }
昼食は、じゃがいも、ご飯、大根葉いりのチヂミ。
夕食のおかずは、鱈のバター焼き、マッシュポテト、にんじんグラッセ、キャベツのマヨネーズあえ、豆腐とワカメの味噌汁。