Golang中通道容量的定义与解析

Golang中通道容量的定义与解析 以下三个语句是否等价?

in := make(chan int)
in := make(chan int, 0)
in := make(chan int, 1)
5 回复

第一条语句是无缓冲通道,容量为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
  1. 当尝试向通道写入第N+2个数据时,还是第N+1个数据时(其中N是给定通道的容量),这个程序会被阻塞?根据输出结果,我可能会认为阻塞发生在第N+2次写入,但我们知道应该是第N+1次,那么真相到底是什么?

  2. 如果我们将输出"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) 是缓冲通道,行为与前两者不同
  • 无缓冲通道提供强同步保证,缓冲通道提供一定的解耦能力

运行上述代码可以看到,在无缓冲通道的情况下,发送操作会等待接收操作;而在缓冲通道中,发送操作可以立即完成(只要缓冲区未满)。

回到顶部