golang channel 封装

对于closed或nil通道,规则如下:

  • 无论收发,nil通道都会阻塞。
  • 不能关闭nil通道。
  • 重复关闭通道,引发panic !
  • 向已关闭通道发送数据,引发 panic!
  • 从已关闭通道接收数据,返回缓冲数据或零值。

nil通道是指没有make的变量。鉴于通道关闭后,所有基于此的阻塞都被解除,可用作通知。

没有判断通道是否已被关闭的直接方法,只能透过收发模式获知,

操作 已关闭的channel nil channel
如果channel中还有数据,可以继续读取;如果channel中没有数据了, 可以读到零值 永久阻塞(deadlock)
panic: send on closed channel 永久阻塞(deadlock)
close panic: close of closed channel panic: close of nil channel

为避免重复关闭,可包装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{}
}

 

 

posted @ 2024-05-11 23:27  codestacklinuxer  阅读(44)  评论(0)    收藏  举报