| 术语 | 解析 |
|---|---|
| goroutine | 协程,比线程轻量级 |
| channel | 管道,用于多个goroutine之间的通信 |
管道的用法
func boring(msg string, c chan string) {
for i := 0; ; i++ {
// 发送信息给管道 (hannel / chan)
// 同时,它也在等待管道的消费者消费完成
c <- fmt.Sprintf("%s %d", msg, i)
time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
}
}
func main() {
c := make(chan string) // 初始化一个管道
go boring("boring!", c)
for i := 0; i < 5; i++ {
// `<-c` 等待 `boring` 方法给它发送值,如果一直没有收到,那么会被阻塞在这一步
fmt.Printf("You say: %q\n", <-c)
}
fmt.Println("You're boring. I'm leaving")
}
上述方法简单说就是boring方法在给管道c发送数据,并且等待另一头,也就是main方法来消费。由于管道中只能够存在一个数据,所以main方法和boring方法在某些程度上是交替运行的。但实际上不完全是,以main方法来说,接受到管道的数据后可以直接进行下一步,而不需要继续等待。
在Go语言中,通道是goroutine与另一个goroutine通信的媒介,并且这种通信是无锁的。换句话说,通道是一种允许一个goroutine将数据发送到另一个goroutine的技术。默认情况下,通道是双向的,这意味着goroutine可以通过同一通道发送或接收数据
协程间通信
type Message struct {
str string // 真正要传输的数据
wait chan bool //
}
func fanIn(inputs ...<-chan Message) <-chan Message {
c := make(chan Message)
for i := range inputs {
input := inputs[i]
go func() {
for {
c <- <-input
}
}()
}
return c
}
// `boring` 是一个返回管道的方法,该管道用于和 `boring` 方法通信
func boring(msg string) <-chan Message {
c := make(chan Message)
waitForIt := make(chan bool)
go func() {
for i := 0; ; i++ {
c <- Message{
str: fmt.Sprintf("%s %d", msg, i),
wait: waitForIt, // 将管道注入到返回值中,用于协程之间的通信
}
time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
// 协程需要在这里等到接收到信息才能够继续执行后面的逻辑
<-waitForIt
}
}()
return c
}
func main() {
// merge 2 channels into 1 channel
c := fanIn(boring("Joe"), boring("Ahn"))
for i := 0; i < 5; i++ {
msg1 := <-c // 等到从管道中接受数据
fmt.Println(msg1.str)
msg2 := <-c
fmt.Println(msg2.str)
// 由于 boring 协程中需要等待 wait 信号才能继续执行,所以这一步能够保证两个协程都能够输出一次数据
msg1.wait <- true // main 协程允许 boring 协程继续执行任务
msg2.wait <- true // main 协程允许 boring 协程继续执行任务
}
fmt.Println("You're both boring. I'm leaving")
}
select 是 Go 中的一个控制结构,类似于用于通信的 switch 语句。每个 case 必须是一个通信操作,要么是发送要么是接收。 select 随机执行一个可运行的 case。如果没有 case 可运行,那么会执行 default 里的操作,如果没有 default,那么它将阻塞,直到有 case 可运行。一个默认的子句应该总是可运行的。
Web页面中,搜索是一个很常见的功能,多数情况下,我们会使用一个微服务来搭建一个搜索服务,如ElasticSearch就是一个单独的服务。在这里,我们不会真的模拟一个ES来处理,反之,我们用一个随机延时的函数来代替它。由于搜索的时间不能够保证,有时候会很快,但有时候也会慢,不管是因为搜索本身就需要时间还是由于IO的耗时。 在这个案例中,我们将会循序渐进来告诉你如何更好的利用goroutine和chan来处理这个问题。除此以外,这里还使用了函数式编程技巧,如果你对这个还不太熟悉,可以先了解一些相关的知识再来继续阅读。