8.7 基于select的多路复用
知识点
- 1.和switch语句稍微有点相似,也会有几个case和最后的default选择支
- 2.每一个case代表一个通信操作(在某个channel上进行发送或者接收)并且会包含一些语句组成的一个语句块
- 3.一个接收表达式可能只包含接收表达式自身,或者包含在一个简短的变量声明中
- 4.select会等待case中有能够执行的case时去执行,执行后,其他通信是不会执行
- 5.没有任何case的select会永远等待下去,写作select{}
- 6.time.Tick所建goroutine依然运行,但没有其他channel接收其值,导致goroutine泄露,因此使用代码所示方式
- 7.对一个nil的channel发送和接收操作会永远阻塞
- 8.在select语句中操作nil的channel永远都不会被select到
代码
func test_select_more() {
/*
time.Tick->goroutine泄露,处理方式
ticker := time.NewTicker(1 * time.Second)
<-ticker.C // receive from the ticker's channel
ticker.Stop() // cause the ticker's goroutine to terminate
*/
//-------11111
//test_countdown_one()
//test_countdown_two()
//test_countdown_thr()
//-------22222
ch := make(chan int, 1)
for i := 0; i < 10; i++ {
select {
case x := <-ch:
fmt.Println(x) // "0" "2" "4" "6" "8"
case ch <- i:
}
}
//
test_concurrent(87)
}
/*
练习 8.8: 使用select来改造8.3节中的echo服务器,为其增加超时,
这样服务器可以在客户端10秒中没有任何喊话时自动断开连接。
*/
//需要netcat和8.3的配合测试
func handleConn_concurrent_87(c net.Conn) {
input := bufio.NewScanner(c)
var wg sync.WaitGroup // number of working goroutines
//abort := make(chan struct{})
abort := make(chan string)
wg.Add(1)
go func() {
defer wg.Done()
for {
select {
case <-time.After(10 * time.Second):
c.Close()
case str := <-abort:
wg.Add(1)
go func(c net.Conn, shut string, delay time.Duration) {
defer wg.Done()
fmt.Fprintf(c, "\t", strings.ToUpper(shut))
time.Sleep(delay)
fmt.Fprintf(c, "\t", shut)
time.Sleep(delay)
fmt.Fprintf(c, "\t", strings.ToLower(shut))
}(c , str, 1*time.Second)
}
}
}()
for input.Scan() {
str := input.Text()
if str == "exit" {
break
}
if len(str) > 0 {
abort <- str
}
}
wg.Wait()
c.Close()
}
func test_countdown_one() {
fmt.Println("Commencing countdown.")
ticker := time.NewTicker(1 * time.Second)
for countdown := 10; countdown > 0; countdown-- {
fmt.Println(countdown)
<-ticker.C
}
ticker.Stop()
fmt.Println("launch()")
}
func test_countdown_two() {
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.")
select {
case <-time.After(10 * time.Second):
// Do nothing.
case <-abort:
fmt.Println("Launch aborted!")
return
}
fmt.Println("launch()")
}
func test_countdown_thr() {
// ...create abort channel...
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.")
ticker := time.NewTicker(1 * time.Second)
for countdown := 10; countdown > 0; countdown-- {
fmt.Println(countdown)
select {
case <-ticker.C:
// Do nothing.
case <-abort:
fmt.Println("Launch aborted!")
return
}
}
ticker.Stop()
fmt.Println("launch()")
}
//练习8.9 隔5秒打印一次桌面文件大小
test_exerise89(5 * time.Second, "/Users/angle/Desktop")
备注
《Go 语言圣经》
- 学习记录所使用的GO版本是1.8
- 学习记录所使用的编译器工具为GoLand
- 学习记录所使用的系统环境为Mac os
- 学习者有一定的C语言基础
代码仓库