首页 / Go 语言入门教程 / channel 基础

Go 语言入门教程

channel 基础

本教程共 80 篇 · 第 56 篇 · 更新于 2026-07-27 · 约 7 分钟阅读

GoGo 入门教程channel通道并发无缓冲channelgoroutine通信

56. channel 基础

本节目标:学会创建 channel、发送和接收数据,理解阻塞机制,掌握无缓冲 channel 的同步用法。

channel 是什么

channel(通道)是 goroutine 之间传数据的管道。Go 有句名言:

不要通过共享内存来通信,而要通过通信来共享内存。

channel 就是这句话的落地工具。你可以把它想成一根管子,一头塞数据进去,另一头取出来。

创建 channel

make 创建,类型是 chan 元素类型

ch := make(chan int)    // 无缓冲 channel
ch2 := make(chan string) // string 类型的 channel

声明的 channel 变量如果没初始化,值是 nil

var ch chan int
fmt.Println(ch == nil) // true

nil channel 发送或接收会永远阻塞,所以用之前一定要 make

发送和接收

<- 操作符,箭头方向就是数据流向:

ch <- 10    // 把 10 发送到 ch
v := <-ch   // 从 ch 接收,赋值给 v

一个完整的例子:

package main

import "fmt"

func main() {
	ch := make(chan int)

	go func() {
		ch <- 42 // 发送
	}()

	v := <-ch // 接收
	fmt.Println(v) // 42
}

主 goroutine 在 v := <-ch 处阻塞,等子 goroutine 把数据塞进来才继续。

阻塞行为

这是 channel 最核心的特性:发送和接收默认都会阻塞

  • 发送方:没人接收就一直等
  • 接收方:没数据来就一直等

这不是 bug,是设计如此。正是这种阻塞让 goroutine 能同步,不用加锁。

无缓冲 channel 的同步

make(chan int) 创建的是无缓冲 channel。它没有暂存空间,发送方必须等到接收方准备好,两个 goroutine 才能「握手」成功。

package main

import (
	"fmt"
	"time"
)

func main() {
	ch := make(chan int)

	go func() {
		fmt.Println("准备发送...")
		ch <- 1
		fmt.Println("发送完成")
	}()

	time.Sleep(time.Second)
	fmt.Println("准备接收")
	fmt.Println("收到:", <-ch)
}

输出大致如下(最后两行顺序可能互换):

准备发送...
准备接收
收到: 1
发送完成

发送方卡在 ch <- 1,直到主 goroutine 来接收才放行。这就是同步效果。

同步使用会死锁

无缓冲 channel 不能在同一个 goroutine 里又发又收,否则死锁:

func main() {
	ch := make(chan int)
	ch <- 1       // 阻塞!没人接收
	fmt.Println(<-ch) // 永远执行不到
}

运行会报错:fatal error: all goroutines are asleep - deadlock!

Warning

无缓冲 channel 的发送和接收必须分属不同的 goroutine。同一 goroutine 内同步收发必然死锁。

接收的两个返回值

接收操作可以拿到第二个返回值,表示是否成功读到数据:

v, ok := <-ch

okfalse 表示 channel 已关闭且没有数据了。这个后面讲 close 时会用到。

关闭 channel

close(ch) 关闭一个 channel。关闭后:

  • 不能再发送(会 panic)
  • 还能继续接收已缓冲的数据
  • 数据收完后,接收返回零值和 false
ch := make(chan int, 2)
ch <- 1
ch <- 2
close(ch)

fmt.Println(<-ch) // 1
fmt.Println(<-ch) // 2
v, ok := <-ch     // 0, false
Note

channel 不是文件,通常不需要手动关闭。只有当你需要通知接收方「没有更多数据了」时才关闭,比如配合 range 遍历。关闭细节下一节细讲。

channel 是引用类型

channel 传给函数时,传的是同一个引用:

func send(ch chan int) {
	ch <- 100
}

func main() {
	ch := make(chan int)
	go send(ch)
	fmt.Println(<-ch) // 100
}

不需要传指针,函数内部操作的 channel 和外面是同一个。

小结

  • make(chan T) 创建无缓冲 channel
  • ch <- v 发送,<-ch 接收,默认阻塞
  • 无缓冲 channel 强制收发双方同步
  • 同一 goroutine 内对无缓冲 channel 收发会死锁
  • 接收有第二个返回值 ok,判断 channel 是否关闭

下一节讲缓冲 channel 和单向 channel。