Appearance
Go 语言最突出之处是并发编程,Unix 老牌黑客罗勃·派克(Rob Pike)在 Google I/O 上的两个分享,可以让你学习到一些并发编程的模式
goroutine
TODO
Channels
TODO
Synchronization
买票卖票 信号量
Buffered channels
TODO
Generator
返回管道的函数
go
func boring(msg string) <-chan string {
c := make(chan string)
go func() {
for i := 0; ; i++ {
c <- fmt.Sprintf("%s %d", msg, i)
time.Sleep(time.Duration(rand.Intn(1e3))
* time.Millisecond)
}
}()
return c
}Fanin
input1 和 input2 相互独立,有人即使阻塞了,也互不影响
go
func fanIn(input1, input2 <-chan string) <-chan string {
c := make(chan string)
go func() {
for {
c <- <-input1
}
}()
go func() {
for {
c <- <-input2
}
}()
return c
}使用 select 改造 fanIn
go
func fanIn(input1, input2 <-chan string) <-chan string {
c := make(chan string)
go func() {
for {
select {
case s := <-input1:
c <- s
case s := <-input2:
c <- s
}
}
}()
return c
}Wait Channels
存储顺序
go
type Message struct {
str string
wait chan bool
}Daisy-chain
go
func f(left, right chan int) {
left <- 1 + <-right
}
...select
流程控制,类似 switch。switch 的 case 是表达式,select 的 case 是通信。
go
select {
case v1 := <-c1:
fmt.Printf("received %v from c1\n", v1)
case v2 := <-c2:
fmt.Printf("received %v from c2\n", v2)
case v3 <- 23:
fmt.Printf("sent %v to c3\n", v3)
default:
fmt.Printf("no one was ready to communicate\n")
}如果 v1 和 v2 都有数据了,那么 select 会【随机】选择一个执行,因此你不能依赖 case 的顺序来编程。
没有 default,select 会阻塞直到存在成功通信的 case,否则 select 不会阻塞。
timeout 特性:for select timeout
For-select loop
TODO
Quit channel
quit 管道:quit := make(chan bool)
接收消息的 quit 管道:quit := make(chan string)
Pipeline
go
func pipeline() {
// input
nums := []int{2, 3, 4, 6}
// stage 1
dataChan := sliceToChannel(nums)
// stage 2
finalChan := sq(dataChan)
// stage 3
for n := range finalChan {
fmt.Println(n)
}
}
func sliceToChannel(nums []int) <-chan int {
out := make(chan int)
go func() {
for _, n := range nums {
out <- n
}
close(out)
}()
return out
}
func sq(in <-chan int) <-chan int {
out := make(chan int)
go func() {
for n := range in {
out <- n * n
}
close(out)
}()
return out
}