4 range和close
发送者可以通过close关闭一个信道来表示没有需要发送的值了。接收者可以通过为接收表达式分配第二个参数来测试信道是否被关闭:如果没有值可以接收且信道已被关闭,那么执行完
v, ok := <-ch
之后,ok会被设置为false
循环for i := range c会不断从信道接收值,直到它被关闭。
注意:只有发送者才能关闭信道,而接收者不能。向一个已经关闭的信道发送数据会引发程序恐慌(panic)。
注意:信道与文件不同,通常情况下无需关闭它们。只有在必须告诉接收者不再有值需要发送的时候才有必要关闭,例如终止一个range循环。
package main
import "fmt"
func fff(ch chan int) {
for i := 0; i < 10; i ++ {
ch <- i
}
close(ch)
}
func main() {
ch := make(chan int, 10)
go fff(ch)
for i := range ch {
fmt.Println(i, "success!")
}
}
5 select 语句
select 语句使一个Go程可以等待多个通信操作
select 会阻塞到某个分支可以继续执行为止,这时就会执行该分支。当多个分支都准备好时会随机选择一个执行。
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)
}
6 默认选择
当select中的其他分支都没有准备好时,default分支就会执行。
为了在尝试发送或接收时不发生阻塞,可使用default分支:
select {
case i := <-c
default:
}
package main
import (
"fmt"
"time"
)
func main() {
tick := time.Tick(100 * time.Millisecond)
boom := time.After(500 * time.Millisecond)
for {
select {
case <-tick:
fmt.Println("tick.")
case <-boom:
fmt.Println("BOOM!")
return
default:
fmt.Println(" .")
time.Sleep(50 * time.Millisecond)
}
}
}
7 sync.Mutex
Go通常使用互斥锁(Mutex)实现互斥。
Go标准库提供了sync.Mutex互斥锁以及两个方法:Lock、Unlock。
可以在代码前调用Lock方法,在代码后调用Unlock方法来保证一段代码的互斥执行。也可以使用defer语句来保证互斥锁一定会被解锁。
package main
import (
"fmt"
"sync"
"time"
)
type SafeCounter struct {
v map[string]int
mux sync.Mutex
}
func (c *SafeCounter) Inc(key string) {
c.mux.Lock()
c.v[key]++
c.mux.Unlock()
}
func (c *SafeCounter) Value(key string) int {
c.mux.Lock()
defer c.mux.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"))
}