goroutine は軽量なスレッド。
go f(x, y)
と書くだけ。
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("hello")
say("world")
}
// hello
// world
// world
// hello
// hello
// world
// hello
// world
// world
// hello
channel 型は <-
を用いて値の送受信を行う。
ch <- v // v をチャネル ch に送信する
v := <- ch // チャネル ch から変数を v に割り当てる
チャネルは make
で作る。
ch := make(chan int)
通常、片方の準備ができるまで送受信はブロックされる。
package main
import "fmt"
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum
}
func main() {
s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
c := make(chan int)
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
go sum(s, c)
x, y := <-c, <-c
z := <-c
fmt.Println(x, y, z)
}
// 55 15 40
// 55 40 15
// 処理終わった順かな?
チャネルはバッファとして使える。 make の 2 つ目の引数にバッファの長さを指定できる。
ch := make(chan int, 10)
バッファ数を超えると deadlock になる。
package main
import "fmt"
func main() {
ch := make(chan bool, 2)
ch <- true
ch <- true
v := <-ch
ch <- true
// ch <- true // この行を入れると deadlock
fmt.Println(len(ch))
fmt.Println(<-ch)
fmt.Println(<-ch)
fmt.Println(v)
fmt.Println(len(ch))
}
// 2
// true
// true
// true
// 0
送信側はチャネルを close
できる。
受信側はチャネルを close
しているか確認できる。
受信する値はなく、かつチャネルが閉じているなら ok
は false
になる。
v, ok := <- ch
ループのときはチャネルを使うとチャネルが閉じるまで値を受信し続ける。
package main
import "fmt"
func fibonacci(n int, c chan int) {
x, y := 0, 1
for i := 0; i < n; i++ {
c <- x
x, y = y, x+y
}
close(c)
}
func main() {
c := make(chan int, 10)
go fibonacci(cap(c), c)
for i := range c {
fmt.Println(i)
}
}
// 0
// 1
// 1
// 2
// 3
// 5
// 8
// 13
// 21
// 34
select
は goroutine を複数の通信操作で待たせる。
case の準備ができるまでブロックする。複数の case が準備できている場合、 case はランダムに実行される。
package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case <-quit:
fmt.Println("quit")
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
fibonacci(c, quit)
}
// 0
// 1
// 1
// 2
// 3
// 5
// 8
// 13
// 21
// 34
// quit
ブロックせずに送受信したいときには default を使う。
package main
import (
"fmt"
"time"
)
func main() {
tick := time.Tick(100 * time.Millisecond)
boom := time.Tick(500 * time.Millisecond)
for {
select {
case <-tick:
fmt.Println("tick")
case <-boom:
fmt.Println("boom")
return
default:
fmt.Println(" .")
time.Sleep(50 * time.Millisecond)
}
}
}
// .
// .
// tick
// .
// .
// tick
// .
// .
// tick
// .
// .
// tick
// .
// .
// boom
コンフリクトを避け、 1 度に 1 つの goroutine だけが変数にアクセスできる。
Golang の標準パッケージは,排他制御を sync.Mutex
と Lock
、 Unlock
で提供している。
package main
import (
"fmt"
"sync"
"time"
)
type SafeCounter struct {
mu sync.Mutex
v map[string]int
}
func (c *SafeCounter) Inc(key string) {
c.mu.Lock()
c.v[key]++
c.mu.Unlock()
}
func (c *SafeCounter) Value(key string) int {
c.mu.Lock()
defer c.mu.Unlock()
return c.v[key]
}
func main() {
c := SafeCounter{
v: make(map[string]int),
}
for i := 0; i < 1000; i++ {
go c.Inc("somekey")
}
time.Sleep(time.Second)
fmt.Println(c.Value("somekey"))
}
// 1000