赞
踩
管道或者是通道。字面意思也就是说是传输的通道或者是管道。
在 Go 语言中,channel 的关键字为 chan
,数据流向的表现方式为 <-
分为两种模式:
双向
– 表现形式为:chan T
,即双向通道。
单向
– 表现形式有两种。分别是:chan <- T
表示只允许发送的通道,T <- chan
表示只允许接收的通道。
除此之外,channel分为缓冲通道
和无缓冲通道
。
// 无缓冲
ch1 := make(chan int)
// 缓冲区为 3
ch2 := make(chan int, 3)
缓冲区大小默认为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之间的数据交换是同步的。
任务完成或是需要停止执行
。会发生死锁的两种情况:
和无缓冲相比,多了一个缓冲区域
。
无缓冲channel用来同步
,有缓冲的channel用来异步
。
在Go语言中,有缓冲的channel允许在阻塞发送和接收操作之前存储一个固定数量的值。这种类型的channel是异步的:只有在缓冲区满时发送操作才会阻塞,只有在缓冲区空时接收操作才会阻塞
。
有缓冲的channel可以作为中间存储,减少直接的依赖
。通过限制缓冲区的大小
,可以在一定程度上控制程序的内存使用,防止因为生产速度远大于消费速度而导致的内存溢出
。足够的数据
积累在缓冲区中之后再批量处理
,可以提高处理效率。缓冲区大小要合适
。太小会导致频繁阻塞,太大会增加内存使用。甚至在生产者速度远大于消费者速度时导致内存泄漏避免在没有接收方时向channel发送数据
,这可能会导致发送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将开始执行退出流程。
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()
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。