package main import ( "errors" "fmt" "reflect" "sync" "syscall" "unsafe" ) var wg sync.WaitGroup var once sync.Once type Str struct { num int pid int } 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 } type ChanInfo struct { Closed bool // 是否关闭 Len uint // channel内数据量 Cap uint // channel容量 Block bool // 是否已经阻塞 } func ChanStatus(c interface{}) (*ChanInfo, error) { v := reflect.ValueOf(c) if v.Type().Kind() != reflect.Chan { return nil, errors.New("type must be channel") } i := (*[2]uintptr)(unsafe.Pointer(&c)) h := (*hchan)(unsafe.Pointer(i[1])) return &ChanInfo{ Cap: h.dataqsiz, Len: h.qcount, Closed: h.closed == 1, Block: h.qcount >= h.dataqsiz, }, nil } func GetCurrentThreadId() int { var user32 *syscall.DLL var GetCurrentThreadId *syscall.Proc var err error user32, err = syscall.LoadDLL("Kernel32.dll") if err != nil { fmt.Printf("syscall.LoadDLL fail: %v\n", err.Error()) return 0 } GetCurrentThreadId, err = user32.FindProc("GetCurrentThreadId") if err != nil { fmt.Printf("user32.FindProc fail: %v\n", err.Error()) return 0 } var pid uintptr pid, _, err = GetCurrentThreadId.Call() return int(pid) } func f1(ch1 chan int) { defer wg.Done() for i := 0; i < 98; i++ { ch1 <- i } ch1 <- 666 ch1 <- 666 close(ch1) } func f2(ch1 chan int, ch2 chan Str) { defer wg.Done() for { //ChanInfo, _ := ChanStatus(ch1) x, ok := <-ch1 if x == 666 && ok == true { break } var s Str s.num = x s.pid = GetCurrentThreadId() //if ChanInfo.Closed { ch2 <- s //} } } func f3(ch1 chan int, ch2 chan Str) { defer wg.Done() for { x, ok := <-ch1 if !ok { break } var s Str s.num = x s.pid = GetCurrentThreadId() ch2 <- s //可能是运行到这 } // close(ch2) once.Do(func() { close(ch2) }) } func main() { ch1 := make(chan int, 100) ch2 := make(chan Str, 100) // ch3 := make(chan Str, 100) wg.Add(3) go f1(ch1) go f2(ch1, ch2) go f2(ch1, ch2) wg.Wait() close(ch2) for { x, ok := <-ch2 if !ok { break } fmt.Println(x.num, x.pid) } // 直接判断状态会有问题,就是运行的时候可能是这个函数先运行了,第一次就直接ok==false跳出了 //于是我拿到了chan的结构信息,想通过长度或者close的状态是否关闭来判断,就又会有可能出现在同一个索引上取值,或者第一个运行到关闭那了,第二个运行到赋值那了 //因为两个结束标记是666 ,第一个线程判断第一个666为真就跳出去执行了,但是第二个线程要么拿到第二个666然后跳出,要么跟第一个同时拿到,因为值被第一个拿了, //第二个就拿了个空,还继续去赋值,还在赋值的时候,第一个又还没执行到close的可能,就panic: send on closed channel //close要在外部关闭, 关闭后不能继续赋值 }