golang 中channel 即使已经关闭了, 仍然可以将channel中的数据读出来, 并不会报错。

一般的写法: data, ok := <- chan,

只有当channel无数据,且channel被close了,才会返回ok=false。

 

 

 



package main

import (
"context"
"fmt"
"reflect"
"time"
"unsafe"
)

func isChanClosed(ch interface{}) bool {
if reflect.TypeOf(ch).Kind() != reflect.Chan {
panic("only channels!")
}

// get interface value pointer, from cgo_export
// typedef struct { void *t; void *v; } GoInterface;
// then get channel real pointer
cptr := *(*uintptr)(unsafe.Pointer(
unsafe.Pointer(uintptr(unsafe.Pointer(&ch)) + unsafe.Sizeof(uint(0))),
))

// this function will return true if chan.closed > 0
// see hchan on https://github.com/golang/go/blob/master/src/runtime/chan.go
// type hchan struct {
// qcount uint // total data in the queue
// dataqsiz uint // size of the circular queue
// buf unsafe.Pointer // points to an array of dataqsiz elements
// elemsize uint16
// closed uint32
// **

cptr += unsafe.Sizeof(uint(0)) * 2
cptr += unsafe.Sizeof(unsafe.Pointer(uintptr(0)))
cptr += unsafe.Sizeof(uint16(0))
return *(*uint32)(unsafe.Pointer(cptr)) > 0
}

func main() {
c := make(chan int, 10)
c <- 1
c <- 2
c <- 3

ctx, cancel := context.WithCancel(context.Background())
close(c)
cancel()
fmt.Println("whether channel is closed:", isChanClosed(c), "\n")

exit:
for {
select {
case i, ok := <-c:
fmt.Println(ok)
if !ok {
fmt.Println("channel closed!")
break
}
fmt.Println(i)

case <-ctx.Done():
break exit
}
}
fmt.Println("aaaaaaaa")
time.Sleep(time.Second * 2)
}


  

我们都知道data, ok := <- chan第一个变量表示读出的数据,第二个变量表示是否成功读取了数据,有意思的是,第二个变量并不用于指示管道的关闭的状态。第二个变量常常被误以为关闭状态是因为它确实和管道状态有关,确切的来说,是和管道缓冲区是否有数据有关。

如果判断golang的channel是否关闭,data, ok := <- chan,当ok不是true的时候,说明是channel关闭了。 那么问题来了,channel关闭了,我们是否可以立马获取到channel被关闭的状态?我想这个问题不少人没有去想吧?为什么有这样的问题?  来自我的一个bug,我期初认为close了一个channel,消费端的goroutine自然是可以拿到channel的关闭状态。然而事实并不是这样的。 只有当channel无数据,且channel被close了,才会返回ok=false。  所以,只要有堆积,就不会返回关闭状态。导致我的服务花时间来消费堆积,才会退出。

————————————————