goroutine和chan
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 if ok == true { 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 //可能是运行到这 } // 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) } }
如果直接用f3的这种方式 可能会造成这种情况发生 两个线程拿到一个索引,第一个拿到,第二个拿到就是空,直接退出,如果在内部关闭的话 就直接报错
0 107812
2 107812
3 107812
4 107812
5 107812
6 107812
7 107812
8 107812
9 107812
10 107812
11 107812
12 107812
13 107812
14 107812
15 107812
16 107812
17 107812
18 107812
19 107812
20 107812
21 107812
22 107812
23 107812
24 107812
25 107812
26 107812
27 107812
28 107812
29 107812
30 107812
31 107812
32 107812
33 107812
34 107812
35 107812
36 107812
37 107812
38 107812
39 107812
40 107812
41 107812
42 107812
43 107812
44 107812
45 107812
46 107812
47 107812
48 107812
49 107812
1 107104