Go 通道

無緩衝區通道

經過make(chan xxx)建立,沒有設置緩衝區大小,這種類型的通道會在兩種狀況下致使阻塞:緩存

  1. 通道中無數據,但執行通道讀操做。
  2. 執行通道寫操做,可是無協程從通道中讀取數據。
// 狀況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. 緩衝區無數據,但執行了通道讀操做。
  2. 緩衝區已滿,但執行了通道寫操做。
//狀況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 + Channel

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

相關文章
相關標籤/搜索