golang的channel探索
1、channel是什么?
管道或者是通道。字面意思也就是说是传输的通道或者是管道。
在 Go 语言中,channel 的关键字为 chan
,数据流向的表现方式为 <-
分为两种模式:
双向
– 表现形式为:chan T
,即双向通道。
单向
– 表现形式有两种。分别是:chan <- T
表示只允许发送的通道,T <- chan
表示只允许接收的通道。
除此之外,channel分为缓冲通道
和无缓冲通道
。
// 无缓冲
ch1 := make(chan int)
// 缓冲区为 3
ch2 := make(chan int, 3)
2、无缓冲channel
缓冲区大小默认为0。
在Go语言中,无缓冲的channel是一种在发送和接收操作之间同步进行的通道。无缓冲channel保证了数据的传递几乎是即时的
,发送操作会阻塞,直到另一端的goroutine执行接收操作,反之亦然。这种特性使得无缓冲channel成为goroutine之间同步操作和通信的理想选择。#### 如何创建?
ch := make(chan int)
如何使用?
var ch = make(chan string)
go func() {
ch <- "包子"
}()
msg := <-ch
fmt.Println(msg)
在上面的代码中,发送操作在另一个goroutine中执行,它会阻塞,直到主goroutine执行接收操作。
特性是什么?
无缓冲channel的一个关键特性是它们在发送和接收数据时提供了同步保证
。
当数据从一个goroutine通过无缓冲channel发送到另一个goroutine时,发送者goroutine会阻塞,直到接收者goroutine接收了数据,这确保了在两个goroutine之间的数据交换是同步的。
使用场景
- 需要确保在数据处理过程中两个goroutine能够同步操作时。
- 用来通知一个goroutine另一个goroutine的事件已经发生,比如
任务完成或是需要停止执行
。
注意事项
会发生死锁的两种情况:
- 没有goroutine接收发送过来的数据
- 没有goroutine发送数据
3、有缓冲channel
和无缓冲相比,多了一个缓冲区域
。
无缓冲channel用来同步
,有缓冲的channel用来异步
。
在Go语言中,有缓冲的channel允许在阻塞发送和接收操作之前存储一个固定数量的值。这种类型的channel是异步的:只有在缓冲区满时发送操作才会阻塞,只有在缓冲区空时接收操作才会阻塞
。
如何使用?
- 当向有缓冲channel发送数据时,如果缓冲区未满,发送操作就会立即完成,并且发送goroutine可以继续执行不会阻塞。如果缓冲区已满,发送操作将会阻塞,直到有空间可用。
- 从有缓冲channel接收数据时,如果缓冲区中有数据,接收操作会立即提取数据并继续执行。如果缓冲区为空,接收操作将会阻塞,直到有数据可读。
应用场景是什么?
- 解耦生产者和消费者的处理速度:当生产者和消费者处理数据的速率不一致时,
有缓冲的channel可以作为中间存储,减少直接的依赖
。 - 流量控制:
通过限制缓冲区的大小
,可以在一定程度上控制程序的内存使用,防止因为生产速度远大于消费速度而导致的内存溢出
。 - 批量处理:有时,等待直到有
足够的数据
积累在缓冲区中之后再批量处理
,可以提高处理效率。
注意事项
缓冲区大小要合适
。太小会导致频繁阻塞,太大会增加内存使用。甚至在生产者速度远大于消费者速度时导致内存泄漏- 即使channel是有缓冲的,也应
避免在没有接收方时向channel发送数据
,这可能会导致发送goroutine永久阻塞
,从而造成goroutine泄漏。
如何判断goroutine是否关闭?
在Go语言中,并没有内置的方法去检测是否关闭
。
有3种模式可以间接地帮助理解goroutine是否已经完成了它的执行任务:
使用sync.WaitGroup
var wg sync.WaitGroup
wg.Add(1)
go func() {
// goroutine完成时调用
defer wg.Done()
// 执行任务
}()
// 等待所有goroutine全部结束(每个都调用done)
wg.Wait()
当wait返回时,表示相关的goroutine已经全部完成了。
使用channel
// 根据Go规范,空结构体不分配内存也就是说不占用空间
// 只是一个信号,没有其他意义
done := make(chan struct{})
// 启动一个goroutine等待信号
go func() {
<-done // 等待信号,不关心传递的数据
fmt.Println("Received signal, exiting.")
}()
// 发送信号
close(done)
使用context
如果你的goroutine是响应取消信号而结束的,那么可以通过检查context的状态来判断goroutine是否已经结束。
ctx, cancel := context.WithCancel(context.Background())
go func() {
// 等待取消信号
<-ctx.Done()
// 清理并退出
}()
// 发出取消信号
cancel()
在这种情况下,当cancel被调用时,依赖于这个context的所有goroutine将开始执行退出流程。
如何优雅的关闭goroutine?
done := make(chan struct{})
go func() {
for{
select {
//等待退出信号
case <-done:
// 收到信号,退出goroutine
return
default:
// 正常执行任务
}
}
}()
// 当需要goroutine结束时,关闭channel 并广播给其他goroutine
close(done)
如果多个goroutine使用了同一个context,context包提供了一个优雅的解决方案。你可以创建一个context,并在需要停止goroutine时取消它。
ctx, cancel := context.WithCancel(context.Background())
go func() {
for {
select {
//检测到取消信号
case <-ctx.Done():
// 退出goroutine
return
default:
// 正常执行任务
}
}
}()
//当需要停止goroutine时,调用cancel通知使用该context的所有goroutine停止
cancel()
var wg sync.WaitGroup
ctx, cancel := context.WithCancel(context.Background())
wg.Add(1)
go func() {
defer wg.Done()
for {
select {
//检测到取消信号
case <-ctx.Done():
// 退出goroutine
return
default:
// 正常执行任务
}
}
}()
//通知使用该context的所有goroutine停止
cancel()
// 等待所有goroutine安全退出
wg.Wait()
原文地址:https://blog.csdn.net/benzun_yinzi/article/details/136343823
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!