通道(channel)是Go语言中goroutine之间通信的主要方式。它类似于一个管道,允许一个goroutine向通道发送值,另一个goroutine从通道接收值。channel是类型化的,即只能传递特定类型的数据。通过channel,可以实现goroutine间的同步和数据共享,遵循“不要通过共享内存来通信,而要通过通信来共享内存”的设计哲学。
使用make(chan 类型)创建无缓冲通道,使用make(chan 类型, 容量)创建有缓冲通道。发送:ch <- value;接收:<-ch。
package main
import "fmt"
func main() {
// 创建int类型的无缓冲通道
ch := make(chan int)
// 启动goroutine发送数据
go func() {
ch <- 42 // 发送
}()
// 主goroutine接收数据
value := <-ch
fmt.Println(value) // 42
}
发送和接收在双方都准备好之前会阻塞,因此无需额外等待机制。
无缓冲通道的发送操作会阻塞,直到另一个goroutine执行接收操作。这使得无缓冲通道成为一个天然的同步点。
func worker(done chan bool) {
fmt.Print("working...")
// 模拟工作
done <- true // 发送完成信号
}
func main() {
done := make(chan bool)
go worker(done)
<-done // 等待worker完成
fmt.Println(" done")
}
有缓冲通道具备一个容量,发送操作只有在缓冲区满时才阻塞,接收操作只有在缓冲区空时才阻塞。
func main() {
ch := make(chan string, 2)
ch <- "hello"
ch <- "world"
// ch <- "extra" // 会导致死锁,缓冲区已满
fmt.Println(<-ch) // hello
fmt.Println(<-ch) // world
}
内置close函数可以关闭通道,表示不再有数据发送。接收方可以通过多返回值判断通道是否关闭。
func producer(ch chan int) {
for i := 0; i < 5; i++ {
ch <- i
}
close(ch)
}
func main() {
ch := make(chan int)
go producer(ch)
// 方式1:显式判断
for {
v, ok := <-ch
if !ok {
break
}
fmt.Println(v)
}
// 方式2:range自动判断关闭
// for v := range ch {
// fmt.Println(v)
// }
}
可以将通道声明为只发送或只接收,增加类型安全性。在函数参数中常用。
// 只发送通道:chan<- int
func sendOnly(ch chan<- int, value int) {
ch <- value
// <-ch // 编译错误
}
// 只接收通道:<-chan int
func receiveOnly(ch <-chan int) int {
return <-ch
}
select用于监听多个通道,哪个通道准备好就执行对应的case。常用于超时控制、非阻塞通信、多路复用等。
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
go func() {
time.Sleep(1 * time.Second)
ch1 <- "来自ch1"
}()
go func() {
time.Sleep(2 * time.Second)
ch2 <- "来自ch2"
}()
for i := 0; i < 2; i++ {
select {
case msg1 := <-ch1:
fmt.Println(msg1)
case msg2 := <-ch2:
fmt.Println(msg2)
}
}
}
select {
case res := <-ch:
fmt.Println(res)
case <-time.After(2 * time.Second):
fmt.Println("超时,未收到数据")
}
select {
case ch <- value:
fmt.Println("发送成功")
case v := <-ch:
fmt.Println("接收到", v)
default:
fmt.Println("通道未就绪,不阻塞")
}
func producer(ch chan<- int, wg *sync.WaitGroup) {
defer wg.Done()
for i := 1; i <= 10; i++ {
ch <- i
fmt.Printf("生产: %d\n", i)
}
close(ch)
}
func consumer(ch <-chan int, wg *sync.WaitGroup) {
defer wg.Done()
for v := range ch {
fmt.Printf("消费: %d\n", v)
}
}
func main() {
ch := make(chan int, 3)
var wg sync.WaitGroup
wg.Add(2)
go producer(ch, &wg)
go consumer(ch, &wg)
wg.Wait()
}
// 死锁示例:主goroutine自己发送并等待接收
func main() {
ch := make(chan int)
ch <- 1 // 阻塞,因为没有其他goroutine接收
<-ch
}
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, jobs <-chan int, results chan<- int, wg *sync.WaitGroup) {
defer wg.Done()
for job := range jobs {
fmt.Printf("worker %d 处理任务 %d\n", id, job)
time.Sleep(time.Millisecond * 200)
results <- job * 2
}
}
func main() {
const numJobs = 10
const numWorkers = 3
jobs := make(chan int, numJobs)
results := make(chan int, numJobs)
var wg sync.WaitGroup
for w := 1; w <= numWorkers; w++ {
wg.Add(1)
go worker(w, jobs, results, &wg)
}
// 发送任务
for j := 1; j <= numJobs; j++ {
jobs <- j
}
close(jobs)
// 等待所有worker完成
wg.Wait()
close(results)
// 收集结果
for res := range results {
fmt.Println("结果:", res)
}
}