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();
	}
}

昼食は、じゃがいも、ご飯、大根葉いりのチヂミ。
夕食のおかずは、鱈のバター焼き、マッシュポテト、にんじんグラッセ、キャベツのマヨネーズあえ、豆腐とワカメの味噌汁。