Channel详解
什么是 ChannelChannel 是 Go 中的一个核心类型可以把它看成一个管道利用通道我们可以在多个 goroutine 之间传递数据。如果说 Goroutine 是 Go 程序并发的执行体Channel 就是它们之间的连接。Channel 是可以让一个 Goroutine 发送特定值到另一个 Goroutine 的通信机制。Channel 像一个传送带或者队列总是遵循**先入先出First In First Out**的规则保证收发数据的顺序。为什么要使用 Channel避免线程安全问题多线程并发编程会带来很多线程安全问题提供同步机制Channel 提供了一种同步的机制确保在数据发送和接收之间的正确顺序和时机通信共享内存提倡通过通信共享内存而不是通过共享内存实现通信。通信顺序进程CSP在很多主流的编程语言中多个线程传递数据的方式一般都是共享内存容易发生竞态问题。为了保证数据交换的正确性必须使用互斥量对内存进行加锁这种做法势必造成性能问题。Go 语言提供了一种不同的并发模型即通信顺序进程Communicating Sequential ProcessesCSP。Goroutine 和 Channel 分别对应 CSP 中的实体和传递信息的媒介Goroutine 之间会通过 Channel 传递数据。Channel 声明与初始化声明 Channelvar变量名chan元素类型示例varch1chanintvarch2chanstring初始化 Channel声明的通道后需要使用make函数初始化之后才能使用ch1:make(chanint)// 无缓冲通道ch2:make(chanstring,10)// 带缓冲通道容量为 10Channel 操作Channel 有三种操作发送send、接收receive和关闭close。发送和接收都使用-符号。发送操作将一个值发送到通道中ch1-100// 发送整数 100 到通道ch2-hello// 发送字符串 hello 到通道接收操作从通道中接收值value:-ch1// 从通道接收值并赋值给变量-ch2// 从通道接收值但忽略关闭操作关闭通道close(ch1)close(ch2)无缓冲通道 vs 有缓冲通道无缓冲通道无缓冲通道发送者和接收者都要存在有一方不存在会导致阻塞。所以说无缓冲的通道又被称为阻塞的通道。使用ch : make(chan int)创建的是无缓冲的通道。特点无缓冲的通道只有在有人接收值的时候才能发送值需要在两个协程间接收和发送。就像你住的小区没有快递柜和代收点快递员给你打电话必须要把这个物品送到你的手中。有缓冲通道解决无缓冲通道阻塞死锁的问题就是使用有缓冲的通道。通过缓存的使用可以尽量避免阻塞提高应用的性能。带缓冲的通道允许发送端的数据发送和接收端的数据获取处于异步状态就是说发送端发送的数据可以放在缓冲区里面可以等待接收端去获取数据而不是立刻需要接收端去获取数据。ch:make(chanint,10)// 创建容量为 10 的有缓冲通道缓冲区操作len(ch)// 获取通道内元素的数量cap(ch)// 获取通道的容量关闭通道关闭后的通道有以下特点对一个关闭的通道再发送值就会导致panic对一个关闭的通道进行接收会一直获取值直到通道为空对一个关闭的并且没有值的通道执行接收操作会得到对应类型的零值关闭一个已经关闭的通道会导致panic。扇入和扇出Fan-Out/Fan-In 模式扇出/扇入模式是并发编程中常用的设计模式特别是在 Go 语言中。它包括两个阶段扇出阶段单个 goroutine 将任务广播给多个工作 goroutine扇入阶段这些工作 goroutine 的结果被聚合到一个单一的通道中。这种模式通过将复杂的并发任务分解成较小、可管理的部分简化了任务。当有大量任务需要同时执行并希望确保结果正确聚合时这种模式尤其有用。扇出示例一个生产者对应多个消费者packagemainimport(fmttime)funcmain(){taskChan:make(chanint,20)// 生产者gofunc(){fori:1;i20;i{taskChan-i}close(taskChan)}()// 消费者 1gofunc(){forv:rangetaskChan{fmt.Println(work 1 任务,v)}}()// 消费者 2gofunc(){fork:rangetaskChan{fmt.Println(work 2 任务,k)}}()// 消费者 3gofunc(){fork:rangetaskChan{fmt.Println(work 3 任务,k)}}()time.Sleep(1*time.Second)fmt.Println(任务结束)}扇入示例多个生产者对应一个消费者packagemainimport(fmttime)funcmain(){taskChan:make(chanint,40)// 生产者 1gofunc(){fori:1;i20;i{taskChan-i}}()// 生产者 2gofunc(){fori:21;i40;i{taskChan-i}}()// 消费者gofunc(){forv:rangetaskChan{fmt.Println(work 任务,v)}fmt.Println(任务处理完成)}()time.Sleep(5*time.Second)close(taskChan)}通道阻塞总结Channel 是 Go 语言实现并发通信的核心机制通过以下要点可以更好地理解和使用 ChannelChannel 是类型化的管道数据按照 FIFO 顺序传递无缓冲通道同步阻塞需要发送和接收双方同时准备好有缓冲通道异步非阻塞数据先存入缓冲区关闭通道后不能再发送数据但可以继续接收扇入扇出模式是处理并发任务的常用设计模式遵循通过通信共享内存的理念避免共享内存带来的线程安全问题。