Skip to content

Instantly share code, notes, and snippets.

@hagino3000
Last active April 21, 2016 05:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hagino3000/da91339a4e1f25b1029fc898a8af885b to your computer and use it in GitHub Desktop.
Save hagino3000/da91339a4e1f25b1029fc898a8af885b to your computer and use it in GitHub Desktop.
gopl chap 8.7

8.7 Multiplexing with select

select statement

select {
	case <- ch1:
		// ...
	case x := <-ch2:
		// ...use x...
	case ch3 <- y:
		//..
	default:
		//...

複数のチャネルを扱う事ができる。

gopl.io/ch8/coundown1

time.Tickチャネルからの入力を待つプログラム

gopl.io/ch8/coundown2

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:
		}
	}
}

gopl.io/ch8/coundown3

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 チャネルが生き続ける

Excercise 8.8

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

8.8 Example: Concurrent Directory Traversal

duコマンドっぽい物

gopl.io/ch8/du1

ディレクトリツリーを辿りながら channelにファイルサイズをpush 全て辿ったら合計してprint

gopl.io/ch8/du2

-v が指定されたら途中経過をprintする

名前を指定しないbreakはselect句を抜けるだけなので、名前付きループを指定してbreakしている

gopl.io/ch8/du3

これまでのはシリアルに全部のディレクトリを走査しているから終るまでに時間がかかる

  • walkDir毎にgoroutineを作る
  • sync.waitGroupを使って全てのgoroutineが終るのを待つ
  • バッファ数20のチャンネルを使ってReadDirの並列数を20に抑える

Exercise 8.9

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

8.9

goroutineを止める手段について

gopl.io/ch8/du4

標準入力があったら止める

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment