select statement
select {
case <- ch1:
// ...
case x := <-ch2:
// ...use x...
case ch3 <- y:
//..
default:
//...
複数のチャネルを扱う事ができる。
time.Tickチャネルからの入力を待つプログラム
time.Afterチャネルとabortチャネルの二つを待ちうける
abort := make(chan struct{})
go func() {
os.Stdin.Read(make([]byte, 1)) // read a single byte
abort <- struct{}{}
}()
select {
case <-time.After(10 * time.Second):
// Do nothing.
case <-abort:
fmt.Println("Launch aborted!")
return
}
launch()
複数の入力がある
ch := make(chan int, 1)
for i := 0; i < 10; i++ {
select {
case x := <-ch:
fmt.Println(x)
case ch<- i:
}
}
}
go leakしてしまう例
abort := make(chan struct{})
go func() {
os.Stdin.Read(make([]byte, 1)) // read a single byte
abort <- struct{}{}
}()
//!+
fmt.Println("Commencing countdown. Press return to abort.")
tick := time.Tick(1 * time.Second)
for countdown := 10; countdown > 0; countdown-- {
fmt.Println(countdown)
select {
case <-tick:
// Do nothing.
case <-abort:
fmt.Println("Launch aborted!")
return
}
}
launch()
for statementの条件を抜けた後も tick チャネルが生き続ける
8.3のecho serverにクライアントが黙ったら10秒でタイムアウトする機能を追加せよ
func handleConn(c net.Conn) {
var countdown = 10
ticker := time.NewTicker(1 * time.Second)
go func() {
for ;countdown > 0; countdown-- {
select {
case <-ticker.C:
fmt.Println(countdown)
}
}
ticker.Stop()
c.Close()
}()
input := bufio.NewScanner(c)
for input.Scan() {
go echo(c, input.Text(), 1*time.Second)
countdown = 10
}
// NOTE: ignoring potential errors from input.Err()
c.Close()
}
duコマンドっぽい物
ディレクトリツリーを辿りながら channelにファイルサイズをpush 全て辿ったら合計してprint
-v が指定されたら途中経過をprintする
名前を指定しないbreakはselect句を抜けるだけなので、名前付きループを指定してbreakしている
これまでのはシリアルに全部のディレクトリを走査しているから終るまでに時間がかかる
- walkDir毎にgoroutineを作る
- sync.waitGroupを使って全てのgoroutineが終るのを待つ
- バッファ数20のチャンネルを使ってReadDirの並列数を20に抑える
func main() {
// ...determine roots...
//!-
flag.Parse()
// Determine the initial directories.
roots := flag.Args()
if len(roots) == 0 {
roots = []string{"."}
}
var waitByRoot sync.WaitGroup
for _, root := range roots {
waitByRoot.Add(1)
go walkFromRoot(root, &waitByRoot)
}
waitByRoot.Wait()
}
func walkFromRoot(rootPath string, wait *sync.WaitGroup) {
defer wait.Done()
//!+
// Traverse each root of the file tree in parallel.
fileSizes := make(chan int64)
var n sync.WaitGroup
n.Add(1)
go walkDir(rootPath, &n, fileSizes)
go func() {
n.Wait()
close(fileSizes)
}()
//!-
// Print the results periodically.
var tick <-chan time.Time
if *vFlag {
tick = time.Tick(500 * time.Millisecond)
}
var nfiles, nbytes int64
loop:
for {
select {
case size, ok := <-fileSizes:
if !ok {
break loop // fileSizes was closed
}
nfiles++
nbytes += size
case <-tick:
printDiskUsage(rootPath, nfiles, nbytes)
}
}
printDiskUsage(rootPath, nfiles, nbytes) // final totals
}
//!-
func printDiskUsage(rootPath string, nfiles int64, nbytes int64) {
fmt.Printf("[%s] %d files %.1f GB\n", rootPath, nfiles, float64(nbytes)/1e9)
}
goroutineを止める手段について
標準入力があったら止める