“通道” 是连接多个 Go 协程的管道。 可以从一个 Go 协程, 将值发送到通道,然后在别的 Go 协程中接收。
package main
import "fmt"
func main() {
// 使用 `make(chan val-type)` 创建一个新的通道。
// 通道类型就 val-type 是他们需要传递值的类型。
// 此处创建名为 message 的通道
messages := make(chan string)
// 使用 `channel <-` 语法 _发送_ 一个值到通道中。这里
// 我们在一个新的 Go 协程中发送 `"hello channel"` 到上面创建的`messages` 通道中。
go func() { messages <- "hello channel" }()
// 使用 `<-channel` 语法从通道中 _接收_ 一个值。这里
// 将接收我们在上面发送的 `"hello channel"` 消息并打印出来。
// 发送和接收操作是阻塞的,直到发送方和接收方都准备完毕。
// 这个特性允许,不使用任何其它的同步操作,来在程序结尾等待
// 消息 `"hello channel"`
msg := <-messages
fmt.Println(msg)
}
死锁问题: messages <- “hello channel” 与 msg := <-messages 必须同时进行,不然会出现死锁
package main
import "fmt"
func main() {
messages := make(chan string)
// 此时已经阻塞在 messages <- "hello channel"
// 解决方案 1. 在messages <- "hello channel"之前,加协程去消费该通道,或者在消费通道之前加协程往通道发送
// 解决方案 2. 给messages通道加缓冲防止阻塞
messages <- "hello channel"
msg := <-messages
fmt.Println(msg)
}
// 运行结果
//fatal error: all goroutines are asleep - deadlock!
//goroutine 1 [chan send]:
//main.main()
在准备发送往通道发送数据之前,开启协程去消费通道
package main
import (
"fmt"
"time"
)
func f1(msg chan string) {
fmt.Println(<-msg)
}
func main() {
messages := make(chan string)
// 此时已经阻塞在 messages <- "hello channel"
// 解决方案 1. 在messages <- "hello channel"之前,加协程去消费该通道
go f1(messages)
messages <- "hello channel"
// 防止协程在执行途中,因主线程的结束而终止
time.Sleep(time.Second * 3)
}
在消费通道之前加协程往通道发送 messages <- “hello channel”
package main
import (
"fmt"
"time"
)
func main() {
messages := make(chan string)
// 此时已经阻塞在 messages <- "hello channel"
// 解决方案 1. 在消费通道之前加协程往通道发送 messages <- "hello channel"
go func() {messages <- "hello channel"}()
fmt.Println( <-messages )
// 防止协程在执行途中,因主线程的结束而终止
time.Sleep(time.Second * 3)
}
通道缓冲 ,给messages通道加缓冲防止阻塞,通道是 无缓冲 的,这意味着只有在对应的接收(
<- chan
),通道准备好接收时,才允许进行发送(chan <-
)。可缓存通道,允许在没有对应接收方的情况下,缓存限定数量的值。
package main
import (
"fmt"
"time"
)
func main() {
// 最多允许缓存一个,此时多发送和多接收,都会引起死锁,所以要保证发送一个,打印一个
messages := make(chan string, 1)
// 此时已经阻塞在 messages <- "hello channel"
// 解决方案 2. 给messages通道加缓冲防止阻塞
messages <- "hello channel"
msg := <-messages
fmt.Println(msg)
// 防止协程在执行途中,因主线程的结束而终止
time.Sleep(time.Second * 3)
}
通道同步: 使用通道来同步 Go 协程间的执行状态。这里是一个使用阻塞的接受方式来等待一个 Go 协程的运行结束。
package main
import "fmt"
import "time"
// 这是将要在 Go 协程中运行的函数。
// `done` 通道将被用于通知其他 Go 协程这个函数已经工作完毕。
func worker(done chan bool) {
fmt.Print("working...")
time.Sleep(time.Second)
fmt.Println("done")
// 发送一个值来通知已经完工。
done <- true
}
func main() {
// 运行一个 worker Go协程,并给予用于通知的通道。
done := make(chan bool, 1)
go worker(done)
// 程序将在接收到通道中 worker 发出的通知前一直阻塞。
fmt.Println("阻塞中")
<-done
}