经过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