鳩舎

レースしない

channel lock

channel でブロックするようなコードが書ける。例えばスレッドの並列数を制限したいとか、そういう簡単な用途で使うのに取り回しがいい。

package main

import (
  "fmt"
  "time"
)

func main() {
  go func() {
    for {
      fmt.Printf(".")
      time.Sleep(100 * time.Millisecond)
    }
  }()

  queue := make(chan int, 3)

  time.Sleep(1 * time.Second)
  queue <- 1
  fmt.Println(1)

  time.Sleep(1 * time.Second)
  queue <- 2
  fmt.Println(2)

  time.Sleep(1 * time.Second)
  queue <- 3
  fmt.Println(3)

  go func() {
    time.Sleep(3 * time.Second)
    <-queue
  }()

  time.Sleep(1 * time.Second)
  // Lock
  queue <- 4
  fmt.Println(4)
}

0.1 秒毎に . が出力されて、 1 秒ごとに 1,2,3 と出力していく。queue は3つまで許容するので、4つめの Lock のところで queue に 4 が送信できなくてロックする。その上にある goroutine で 3 秒後に queue から 1 つ受信して捨てると、 4 が送信できるようになって動く。

出力はこんな感じになる

..........1
..........2
..........3
..............................4

4 のときだけ 3 秒ロックしてるのがわかる。ところでこの queue から受信するコードをなくすとどうだろう。ついでに邪魔だから . を出力していた goroutine もなくしてしまおう。

package main

import (
  "fmt"
  "time"
)

func main() {
  queue := make(chan int, 3)

  time.Sleep(1 * time.Second)
  queue <- 1
  fmt.Println(1)

  time.Sleep(1 * time.Second)
  queue <- 2
  fmt.Println(2)

  time.Sleep(1 * time.Second)
  queue <- 3
  fmt.Println(3)

  time.Sleep(1 * time.Second)
  queue <- 4
  fmt.Println(4)
}

例外が出る。

1
2
3
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
    /Users/rosylilly/tmp/go_channel_test/main.go:24 +0x22e
exit status 2

全部の goroutine が 寝てて、何もしてないからデッドロック状態だ、ということらしい。デッドロック検知して自分で死ぬ。モダンな感じだ。