golang中的单向channel和nil值channel

在阅读client-go代码时发现源码对channel的部分使用方式不太理解,一个是单项channel的使用,一个是nil值channel的使用,于是搜索总结一下。

在golang中可以定义channel为双向的,也可以将channel定义为只能接收或只能发送类型的,如下面所示:

// You can edit this code!
// Click here and start typing.
package main

func main() {
	// 普通双向的
	var normalCh chan interface{}
	// 只能发送
	var sendOnlyCh chan<- interface{}
	// 只能接收
	var receiveOnlyCh <-chan interface{}
	<-sendOnlyCh
	receiveOnlyCh <- 1
}

编译上述代码,将会得到下列错误:

./prog.go:12:2: invalid operation: <-sendOnlyCh (receive from send-only type chan<- interface {})
./prog.go:13:16: invalid operation: receiveOnlyCh <- 1 (send to receive-only type <-chan interface {})

Go build failed.

可以看到读取只发送channel和写只接收channel都会编译出错,但是普通的channel则既可以读取又可以发送,如下面这个例子,这个例子在goroutine中定义了一个双向读写的channel,但是返回值类型是只读类型,在这个goroutine中可以对这个channel进行读写,但是调用F()通过返回值拿到的channel只能读取:

func F() <-chan int {
    // Create a regular, two-way channel.
    c := make(chan int)

    go func() {
        defer close(c)

        // Do stuff
        c <- 123
    }()

    // Returning it, implicitely converts it to read-only,
    // as per the function return value.
    return c
}

go中,可以对nil值channel进行读写操作,当对值为nil的channel进行读取操作时会阻塞,但是对值为nil值的channel调用close()会panic。使用nil值channel可以帮助在select中禁用某个select分支,因为阻塞了所以都不会进入分支语句。

下面是client-go中对nil值channel和单向channel的使用的函数代码:

func (p *processorListener) pop() {
	defer utilruntime.HandleCrash()
	defer close(p.nextCh) // Tell .run() to stop

	var nextCh chan<- interface{}
	var notification interface{}
	for {
		select {
		case nextCh <- notification:
			// Notification dispatched
			var ok bool
			notification, ok = p.pendingNotifications.ReadOne()
			if !ok { // Nothing to pop
				nextCh = nil // Disable this select case
			}
		case notificationToAdd, ok := <-p.addCh:
			if !ok {
				return
			}
			if notification == nil { // No notification to pop (and pendingNotifications is empty)
				// Optimize the case - skip adding to pendingNotifications
				notification = notificationToAdd
				nextCh = p.nextCh
			} else { // There is already a notification waiting to be dispatched
				p.pendingNotifications.WriteOne(notificationToAdd)
			}
		}
	}
}

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注