Golang中通道容量的定义与解析
Golang中通道容量的定义与解析 以下三个语句是否等价?
in := make(chan int)
in := make(chan int, 0)
in := make(chan int, 1)
第一条语句是无缓冲通道,容量为0;第二条语句是缓冲通道,容量为0;第三条语句是容量为1的通道。
更多关于Golang中通道容量的定义与解析的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
打印输出的顺序可能"出错",因为您在底部循环打印之前就从通道读取数据,这样goroutine就有可能在循环打印之前成功向通道写入数据。
你可以自行测试。
func TestChanSize(t *testing.T) {
c1 := make(chan int)
c2 := make(chan int, 0)
c3 := make(chan int, 1)
t.Log("c1:size=", unsafe.Sizeof(c1), "len=", len(c1), "cap=", cap(c1))
t.Log("c2:size=", unsafe.Sizeof(c2), "len=", len(c2), "cap=", cap(c2))
t.Log("c3:size=", unsafe.Sizeof(c3), "len=", len(c3), "cap=", cap(c3))
}
//c1:size= 8 len= 0 cap= 0
//c2:size= 8 len= 0 cap= 0
//c3:size= 8 len= 0 cap= 1
-
当尝试向通道写入第N+2个数据时,还是第N+1个数据时(其中N是给定通道的容量),这个程序会被阻塞?根据输出结果,我可能会认为阻塞发生在第N+2次写入,但我们知道应该是第N+1次,那么真相到底是什么?
-
如果我们将输出"generator finish"的那行代码放在**close(out)**之后,那么程序的输出将不总是包含"generator finish"——这是为什么?
package main
import (
"fmt"
)
func main() {
in := make(chan int, 1)
go func(out chan<- int) {
for i := 0; i < 3; i++ {
fmt.Println("before", i)
out <- i
fmt.Println("after", i)
}
fmt.Println("generator finish")
close(out)
}(in)
for i := range in {
fmt.Println("_____get", i)
}
}
before 0 after 0 before 1 after 1 before 2 _____get 0 _____get 1 _____get 2 after 2 generator finish
在Go语言中,这三个通道声明语句并不等价,主要区别在于通道的容量(buffer size)不同,这会直接影响通道的同步行为。
1. in := make(chan int)
- 创建一个无缓冲通道
- 容量为0
- 发送和接收操作必须同时准备好才能完成,否则会阻塞
2. in := make(chan int, 0)
- 显式指定容量为0
- 与第一个声明完全等价,都是无缓冲通道
3. in := make(chan int, 1)
- 创建容量为1的缓冲通道
- 可以暂存1个元素而不阻塞发送方
示例代码演示差异:
package main
import (
"fmt"
"time"
)
// 无缓冲通道示例
func unbufferedExample() {
ch := make(chan int) // 等价于 make(chan int, 0)
go func() {
fmt.Println("准备发送数据...")
ch <- 42 // 这里会阻塞,直到有接收方
fmt.Println("数据发送完成")
}()
time.Sleep(1 * time.Second)
fmt.Println("准备接收数据...")
<-ch
fmt.Println("数据接收完成")
}
// 缓冲通道示例
func bufferedExample() {
ch := make(chan int, 1)
go func() {
fmt.Println("准备发送数据...")
ch <- 42 // 不会阻塞,因为缓冲区有空位
fmt.Println("数据发送完成")
}()
time.Sleep(1 * time.Second)
fmt.Println("准备接收数据...")
<-ch
fmt.Println("数据接收完成")
}
func main() {
fmt.Println("=== 无缓冲通道行为 ===")
unbufferedExample()
fmt.Println("\n=== 缓冲通道行为 ===")
bufferedExample()
}
关键区别总结:
make(chan int)和make(chan int, 0)完全等价,都是无缓冲通道make(chan int, 1)是缓冲通道,行为与前两者不同- 无缓冲通道提供强同步保证,缓冲通道提供一定的解耦能力
运行上述代码可以看到,在无缓冲通道的情况下,发送操作会等待接收操作;而在缓冲通道中,发送操作可以立即完成(只要缓冲区未满)。

