經過make(chan xxx)
建立,沒有設置緩衝區大小,這種類型的通道會在兩種狀況下致使阻塞:緩存
// 狀況1 func ReadNoDataFromNoBufCh() { noBufCh := make(chan int) <-noBufCh println("read from no buffer channel success") } // 狀況2 func WriteNoBufCh() { ch := make(chan int) ch <- 1 println("write success no block") }
經過make(chan xxx, int)
建立,並設置了緩衝區大小。若是緩存區未滿,則寫入後會當即返回。若是緩衝區有數據,讀取後也會當即返回。這種類型的通道會在兩種狀況下致使阻塞:code
//狀況1 func ReadNoDataFromBufCh() { bufCh := make(chan int, 1) <-bufCh // 緩衝區無數據,讀取失敗 println("read from buffer channel success") } //狀況2 func WriteBufCh() { ch := make(chan int, 1) ch <- 1 // 有緩衝區,寫入後當即返回 ch <- 2 // 緩衝區已滿,沒法寫入 println("write success no block") }
select會隨機選擇一個未阻塞的通道,若是都阻塞了,則等待直到有一個通道不阻塞。咱們也能夠經過default分支來實現默認的無阻塞操做,具體代碼以下:協程
func ReadNoDataFromNoBuffChWithSelect() { noBufCh := make(chan int) if v, err := ReadWithSelect(noBufCh); err != nil { fmt.Println(err) } else { fmt.Printf("read: %d\n", v) } } func ReadNoDataFromBufChWithSelect() { bufCh := make(chan int, 1) if v, err := ReadWithSelect(bufCh); err != nil { fmt.Println(err) } else { fmt.Printf("read: %d\n", v) } } func ReadWithSelect(ch chan int) (int, error) { select { case x := <-ch: return x, nil default: return 0, errors.New("channel has no data") } } func WriteNoBufChWithSelect() { ch := make(chan int) if err := WriteChWithSelect(ch); err != nil { fmt.Println(err) } else { println("success") } } func WriteBufChButFullWithSelect() { ch := make(chan int, 1) ch <- 100 if err := WriteChWithSelect(ch); err != nil { fmt.Println(err) } else { println("success") } } func WriteChWithSelect(ch chan int) error { select { case ch <- 1: return nil default: return errors.New("channel blocked, can not write") } }
使用default實現的無阻塞讀寫有一個缺點:當通道不可讀寫時,會當即返回。可是實際場景中,咱們可能須要等待一段時間後再返回,使用定時器替代default能夠解決這個問題,給通道必定的容忍時間,代碼以下:it
func ReadWithSelectAndTimer(ch chan int) (int, error) { timeout := time.NewTimer(time.Microsecond * 500) select { case x := <-ch: return x, nil case <-timeout.C: // 若是500ms內沒法讀寫,就即刻返回 return 0, errors.New("read time out") } } func WriteChWithSelectAndTimer(ch chan int) error { timeout := time.NewTimer(time.Microsecond * 500) select { case ch <- 1: return nil case <-timeout.C: // 若是500ms內沒法讀寫,就即刻返回 return errors.New("write time out") } }
注意:若是select寫在循環語句當中,而且也用了定時通道,不要在select中每次都NewTimer,在循環外面建立定時器,避免頻繁建立。select