上卷 程序设计
中卷 标准库
- bufio 1.18
- bytes 1.18
- io 1.18
- container 1.18
- encoding 1.18
- crypto 1.18
- hash 1.18
- index 1.18
- sort 1.18
- context 1.18
- database 1.18
- connection
- query
- queryrow
- exec
- prepare
- transaction
- scan & null
- context
- tcp
- udp
- http
- server
- handler
- client
- h2、tls
- url
- rpc
- exec
- signal
- embed 1.18
- plugin 1.18
- reflect 1.18
- runtime 1.18
- KeepAlived
- ReadMemStats
- SetFinalizer
- Stack
- sync 1.18
- atomic
- mutex
- rwmutex
- waitgroup
- cond
- once
- map
- pool
- copycheck
- nocopy
- unsafe 1.18
- fmt 1.18
- log 1.18
- math 1.18
- time 1.18
- timer
下卷 运行时
源码剖析
附录
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
9.2 通道
鼓励使用 CSP 通道,以通信来代替内存共享,实现并发安全。
通道(channel)行为类似消息队列。不限收发人数,不可重复消费。
Don't communicate by sharing memory, share memory by communicating.
CSP: Communicating Sequential Process.
同步
没有数据缓冲区,须收发双方到场直接交换数据。
- 阻塞,直到有另一方准备妥当或通道关闭。
- 可通过
cap == 0
判断为无缓冲通道。
func main() { quit := make(chan struct{}) data := make(chan int) go func() { data <- 11 }() go func() { defer close(quit) println(<- data) println(<- data) }() data <- 22 <- quit }
异步
通道自带固定大小缓冲区。有数据或有空位时,不会阻塞。
- 用
cap
、len
获取缓冲区大小和当前缓冲数。
func main() { quit := make(chan struct{}) data := make(chan int, 3) data <- 11 data <- 22 data <- 33 println(cap(data), len(data)) // 3 3 go func() { defer close(quit) println(<- data) println(<- data) println(<- data) println(<- data) // block }() data <- 44 <- quit }
缓冲区大小仅是内部属性,不属于类型组成部分。
通道变量本身就是指针,可判断是否为同一对象或 nil
。
func main() { var a, b chan int = make(chan int, 3), make(chan int) var c chan bool println(a == b) // false println(c == nil) // true println(a, unsafe.Sizeof(a)) // 0xc..., 8 }
关闭
对于 closed
或 nil
通道,规则如下:
- 无论收发,
nil
通道都会阻塞。 - 不能关闭
nil
通道。
- 重复关闭通道,引发
panic
!
- 向已关闭通道发送数据,引发
panic
! - 从已关闭通道接收数据,返回缓冲数据或零值。
提示,
nil
通道是指没有make
的变量。鉴于通道关闭后,所有基于此的阻塞都被解除,可用作通知。
没有判断通道是否已被关闭的直接方法,只能透过收发模式获知。
func main() { c := make(chan int) close(c) // close(c) // ~~~~~ panic: close of closed channel // 不会阻塞,返回零值。 println(<- c) // 0 println(<- c) // 0 }
func main() { c := make(chan int, 2) c <- 1 close(c) // 不会阻塞,返回零值。 println(<- c) // 1 println(<- c) // 0 println(<- c) // 0 }
为避免重复关闭,可包装 close
函数。
也可以类似方式封装 send
、 recv
操作。
func closechan[T any](c chan T) { defer func(){ recover() }() close(c) } func main() { c := make(chan int, 2) closechan(c) closechan(c) }
保留关闭状态。注意,为并发安全,关闭和获取关闭状态应保持同步。
可使用
sync.RWMutex
、sync.Once
优化设计。
type Queue[T any] struct { sync.Mutex ch chan T cap int closed bool } func NewQueue[T any](cap int) *Queue[T] { return &Queue[T]{ ch: make(chan T, cap), } } func (q *Queue[T]) Close() { q.Lock() defer q.Unlock() if !q.closed { close(q.ch) q.closed = true } } func (q *Queue[T]) IsClosed() bool { q.Lock() defer q.Unlock() return q.closed } // --------------------------------- func main() { var wg sync.WaitGroup q := NewQueue[int](3) for i := 0; i < 10; i++ { wg.Add(1) go func() { defer wg.Done() defer q.Close() println(q.IsClosed()) }() } wg.Wait() }
利用 nil
通道阻止退出。
func main() { <-(chan struct{})(nil) // select{} }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论