ゼロから学ぶGo言語プログラミング(14) A Tour of Go 62~66
A Tour of Goの続き。
62.Concurrency
やっとGoのうりのひとつ、並行性。
63.Goroutines
goroutine (ゴルーチン)は、Goのランタイムに管理される軽量なスレッドです。
Goはgoroutineという軽量なスレッドを簡単に生成できて、任意の関数を
go f()
とするだけで、新たなgoroutineで実行できる。
goroutine間は同じアドレス空間ということは、他の言語のマルチスレッドみたいに、メモリアクセスを制御しないといけない。 でも次のスライドの方法で、良い制御ができる、と。
しかし、サンプルにあるコードがどうも理解できない。
package main import ( "fmt" "time" ) func say(s string) { for i := 0; i < 5; i++ { time.Sleep(100 * time.Millisecond) fmt.Println(s) } } func main() { go say("world") say("hello") }
これのsay()からforを取り除くと、"hello"のみが出力される。 あるいはmain()でどちらのsay()もgo say()とすると、何も出力されない。
fmtに関するメモリの概念を理解できていないからわからないんだろうか。
64.Channels
このchannelが、並行処理の同期の良い方法らしい。
とりあえず channel は次のようなもの。
- 値の送受信を行うための直通ルート
- 送受信には"<-"という記号を使用し、これをチャンネルオペレータと呼ぶ
- channelは make(chan 型)というように、makeでの生成が必要
- 引数にchannel型を指定する場合は、"f(c chan int)"のように書く
概念はなんとなくは分かるけれど、サンプルコードにはchannelが複数回出てきて混乱した。 でも、channelはあくまで経路でしかないと考えれば、すっきりした。
つまり生成したchannelをgoroutineに渡し、goroutine内では結果をreturnではなく、そのchannelに渡す。 goroutineの外のコードでは、そのchannelを引数に取る処理があるが、channelからデータが送られてくるまでは待機する、という感じらしい。
ということで、自分でchannelを使ってみる。
package main import ( "fmt" "time" ) func think(x int, y int, t int, c chan int) { time.Sleep(time.Duration(t) * time.Millisecond) c <- x + y } func main() { c := make(chan int, 2) go think(5, 5, 1000, c) go think(10, 10, 500, c) fmt.Println(<-c) fmt.Println(<-c) }
んー、なんとなくは分かったけれど、どういう基準でchannelを作るべきかとか、どういう状況で利用すべきかはまだ見えない。
65.Buffered Channels
channelはバッファサイズを指定して生成でき、バッファサイズを持つchannelをBuffered Channelsと呼ぶらしい。
バッファの単位は解説されていないけれど、channelに対する送受信単位の回数ということだろうか。
バッファが使えると、送受信のそれぞれがバッファを緩衝にして、送信が追いつかなければ受信はバッファから受信するか、バッファが空なら待機。 受信が追いつかなければ、送信はバッファにデータを積み、バッファもいっぱいであれば待機、という使い方ができるのかな。
66.Range and Close
channelを閉じるcloseは、シンプルに
close(chan)
とだけ。
閉じたchannelは受けてで検出。 もし閉じたchannelに送信すると、pannicというランタイムエラーの一種になる、と。
チャネルは、ファイルと同じようなものではありません。 通常は、閉じる必要はありません。
これ、作ったchannelはいつ破棄されるんだろう。 明示的に手動で破棄?