go检查channel是否关闭

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。 所以,只要有堆积,就不会返回关闭状态。导致我的服务花时间来消费堆积,才会退出。

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

版权声明:本文为CSDN博主「github_zwl」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/github_34457546/article/details/109687879

参考: https://blog.csdn.net/github_34457546/article/details/109687879

https://stackoverflow.com/questions/16105325/how-to-check-a-channel-is-closed-or-not-without-reading-it

----------------------------------------------