這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
GOLANG CHAN
chan是golang中非常重要的一個東西,用來做goroutine的通訊,因為golang程式必然會有多個goroutine,如何同步這些goroutine就很重要了。
使用chan時有幾個心得:
- 首先,永遠是符號
<-
進行讀取或者寫入,譬如v,ok := <-c
是讀取,而c <- v
是寫入。
- 其次,讀取時,如果沒有ok,也是可以讀取的。不過如果closed也是能讀的,沒有賦值而已;如果要知道是否closed得加ok,也就是除非chan永遠不關閉,否則讀取應該用
v,ok := <-c
而不是用v := <-c
的方式。
- 再次,不能向closed的chan寫入,所以一般寫入時需要用一個訊號的chan(一般buffer為1),來判斷是否寫入或者放棄,用select判斷是寫入成功了,還是正在關閉需要放棄寫入。
- 最後,如果closed後,chan有資料,ok還是true的,直到chan沒有資料了才false。
讀寫Chan
永遠是符號<-
進行讀取或者寫入,譬如v,ok := <-c
是讀取,而c <- v
是寫入。
c := make(chan int, 1)c <- 10 // 寫入chanv := <- c // 從chan中讀取
下面的例子判斷chan是否關閉:
c := make(chan int, 1)c <- 10v,ok := <- c // 讀取,v=10,ok=trueclose(c)v,ok := <- c // 讀取,v=0,ok=false
如果寫不進去就丟棄,可以用select:
c := make(chan int, 1)select {case c <- 10: // c中放入了10,因為chan的buffer為1default: }select {case c <- 11:default: // c中只有10,沒有11}select {case v,ok := <- c: // 讀出來一個,v=10, ok=truedefault:}select {case v,ok := <- c:default: // 沒有可讀的,走這個分支}
還可以用逾時之類的,也是一個chan,time.After(xxx)
返回的就是chan。
判斷closed
讀取時,如果沒有ok,也是可以讀取的。不過如果closed也是能讀的,沒有賦值而已;如果要知道是否closed得加ok,也就是除非chan永遠不關閉,否則讀取應該用v,ok := <-c
而不是用v := <-c
的方式。
c := make(chan int, 1)c <- 10close(c)v := <- c // c=10,讀取出來一個v = <- c // c=0,實際上沒有讀出來,但是判斷不了
c := make(chan int, 1)c <- 10close(c)v,ok := <- c // c=10,ok=true,讀取出來一個v,ok = <- c // c=0,ok=false,實際上沒有讀出來
寫入chan
不能向closed的chan寫入,所以一般寫入時需要用一個訊號的chan,來判斷是否寫入或者放棄,用select判斷是寫入成功了,還是正在關閉需要放棄寫入。
type TcpListeners struct { conns chan *net.TCPConn closing chan bool wait *sync.WaitGroup}func NewTcpListeners(addrs []string) (v *TcpListeners, err error) { v = &TcpListeners{ addrs: addrs, conns: make(chan *net.TCPConn), closing: make(chan bool, 1), wait: &sync.WaitGroup{}, } return}// Listen at addrs format as netowrk://laddr, for example,// tcp://:1935, tcp4://:1935, tcp6://1935, tcp://0.0.0.0:1935func (v *TcpListeners) ListenTCP() (err error) { for _, addr := range v.addrs { vs := strings.Split(addr, "://") network, laddr := vs[0], vs[1] if l, err := net.Listen(network, laddr); err != nil { return nil,err } else { v.listeners = append(v.listeners, l.(*net.TCPListener)) } } v.wait.Add(len(v.listeners)) for i, l := range v.listeners { addr := v.addrs[i] go func(l *net.TCPListener, addr string) { defer v.wait.Done() for { var conn *net.TCPConn if conn, err = l.AcceptTCP(); err != nil { return } select { case v.conns <- conn: case c := <-v.closing: v.closing <- c conn.Close() } } }(l, addr) } return}func (v *TcpListeners) AcceptTCP() (c *net.TCPConn, err error) { var ok bool if c,ok = <- v.conns; !ok { return nil, ListenerDisposed } return}func (v *TcpListeners) Close() (err error) { // unblock all listener internal goroutines v.closing <- true // interrupt all listeners. for _, v := range v.listeners { if r := v.Close(); r != nil { err = r } } // wait for all listener internal goroutines to quit. v.wait.Wait() // clear the closing signal. _ = <-v.closing // close channels to unblock the user goroutine to AcceptTCP() close(v.conns) return}
這樣在關閉Listener時,不會導致ListenTCP的goroutine寫入closed的chan而導致錯誤。
Closed Chan
如果closed後,chan有資料,ok還是true的,直到chan沒有資料了才false。
c := make(chan int, 1)c <- 10close(c)v,ok := <- c // v=10,ok=true,雖然c關閉了,但是有資料,ok依然是truev,ok <- c // v=0,ok=false,讀失敗了。