golang channel
作者:@daemon365
本文为作者原创,转载请注明出处:https://www.cnblogs.com/daemon365/p/15161461.html
什么是channel
channels 是一种类型安全的消息队列,充当两个 goroutine 之间的管道,将通过它同步的进行任意资源的交换。chan 控制 goroutines 交互的能力从而创建了 Go 同步机制。当创建的 chan 没有容量时,称为无缓冲通道。反过来,使用容量创建的 chan 称为缓冲通道。
要了解通过 chan 交互的 goroutine 的同步行为是什么,我们需要知道通道的类型和状态。根据我们使用的是无缓冲通道还是缓冲通道,场景会有所不同,所以让我们单独讨论每个场景。
无缓冲管道
make : ch := make(chan struct{})
无缓冲 chan 没有容量,因此进行任何交换前需要两个 goroutine 同时准备好。当 goroutine 试图将一个资源发送到一个无缓冲的通道并且没有goroutine 等待接收该资源时,该通道将锁住发送 goroutine 并使其等待。当 goroutine 尝试从无缓冲通道接收,并且没有 goroutine 等待发送资源时,该通道将锁住接收 goroutine 并使其等待。
无缓冲信道的本质是保证同步。
无缓冲channel在消息发送时需要接收者就绪。声明无缓冲channel的方式是不指定缓冲大小。以下是一个列子:
package main import ( "sync" "time" ) func main() { c := make(chan string) var wg sync.WaitGroup wg.Add(2) go func() { defer wg.Done() c <- `foo` }() go func() { defer wg.Done() time.Sleep(time.Second * 1) println(`Message: `+ <-c) }() wg.Wait() }
第一个协程会在发送消息foo
时阻塞,原因是接收者还没有就绪:这个特性在标准文档中描述如下:
如果缓冲大小设置为0或者不设置,channel为无缓冲类型,通信成功的前提是发送者和接收者都处于就绪状态。
effective Go文档也有相应的描述:
无缓冲channel,发送者会阻塞直到接收者接收了发送的值。
为了更好的理解channel的特性,接下来我们分析channel的内部结构。
package main import ( "sync" "time" "fmt" ) func main() { c := make(chan string) var wg sync.WaitGroup wg.Add(2) go func() { defer wg.Done() c <- "foo" }() go func() { defer wg.Done() time.Sleep(time.Second) fmt.Println("message:" + <- c) }() wg.Wait() } /* foo */
有缓冲管道
buffered channel 具有容量,因此其行为可能有点不同。当 goroutine 试图将资源发送到缓冲通道,而该通道已满时,该通道将锁住 goroutine并使其等待缓冲区可用。如果通道中有空间,发送可以立即进行,goroutine 可以继续。当goroutine 试图从缓冲通道接收数据,而缓冲通道为空时,该通道将锁住 goroutine 并使其等待资源被发送。
package main import ( "sync" "time" "fmt" ) func main() { c := make(chan string,2) var wg sync.WaitGroup wg.Add(2) go func(){ defer wg.Done() c <- "foo" c <- "bar" }() go func(){ defer wg.Done() time.Sleep(time.Second) fmt.Println("mesage:" + <- c) fmt.Println("message:" + <- c) }() wg.Wait() } /* mesage:foo message:bar */
追踪耗时
通过Go工具trace中的synchronization blocking profile
来查看测试程序被同步原语阻塞所消耗的时间。接收时的耗时对比:无缓冲channel为9毫秒,缓冲大小为50的channel为1.9毫秒。
发送时的耗时对比:有缓冲channel将耗时缩小了五倍。
- Send 先于 Receive 发生。
- 好处: 延迟更小。
- 代价: 不保证数据到达,越大的 buffer,越小的保障到达。buffer = 1 时,给你延迟一个消息的保障。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?